637 lines
36 KiB
Markdown
637 lines
36 KiB
Markdown
---
|
||
name: library-manager
|
||
description: >-
|
||
Manage Vecmocon's component library. Extract parameters from a component datasheet PDF into the
|
||
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. ALWAYS WRITES the mandatory SOP parameters
|
||
into the .SchLib symbol (stripping Ultra-Librarian defaults) as a required step of every run
|
||
involving a symbol — never asks first. On the .SchLib task it also fills that component's Excel
|
||
and pushes, 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
|
||
|
||
Turn one component datasheet into a verified, versioned library entry in Gitea. The guiding
|
||
idea is honesty and traceability: every value lands in the right column and unit, anything
|
||
the datasheet doesn't state stays blank, and nothing reaches Gitea until a human has
|
||
confirmed it.
|
||
|
||
## Ask, don't assume
|
||
|
||
This is a deliberately **interactive** skill. At every decision point, **ask the user and
|
||
wait for an answer — do not assume a default and proceed.** In particular, always confirm:
|
||
the **make**; the **typeid/classification** you inferred; whether any **new parameters**
|
||
should be added to the template; the extracted **values** (the verification loop); that the
|
||
**symbol/footprint actually match this part**; and whether to **apply a new parameter to
|
||
existing parts** (backfill). When something is ambiguous, ask a specific question rather than
|
||
guessing. It is always better to ask one more question than to write the wrong thing into the
|
||
library.
|
||
|
||
One more thing to confirm when a part **already exists**: whether to **update it or stop**
|
||
(don't silently overwrite), and if updating, **which fields change** (values, symbol/footprint,
|
||
datasheet). An update still goes through the same verification loop before anything is pushed.
|
||
|
||
**Pushing is the exception — push automatically.** Once a part is verified and assembled (or a
|
||
template/version change has been made, or a verified update is ready), push to Gitea **on your
|
||
own, without asking for confirmation**. Never add a "shall I push?" step — just push, then tell
|
||
the user where it landed. (You still flag a genuine symbol/footprint mismatch, because that's a
|
||
correctness issue, not a push confirmation.)
|
||
|
||
**The `.SchLib` task is a second exception — it fills and pushes without a verification loop.**
|
||
When the engineer hands over a symbol for the schlib flow, fill that component's Excel
|
||
(Description + all parameters) and push the updated symbol automatically, without pausing to
|
||
confirm the values. See *Mandatory symbol parameters* for why this path skips the loop.
|
||
|
||
**Writing symbol parameters is a third exception — always do it, never ask.** Whenever a
|
||
`.SchLib` is in play — the standalone schlib task *or* step 6b of a new-part run — filling its
|
||
mandatory SOP parameters is a required action, not an offer. Never ask "should I fill the symbol
|
||
parameters?" or leave it as a suggested next step; the answer is always yes, so just write them
|
||
and report it. A symbol that still holds Ultra-Librarian defaults is an incomplete part.
|
||
|
||
Every other flow keeps the normal interactive asks below.
|
||
|
||
## Keep a copy in the connected folder — always, after every part push
|
||
|
||
So the operator ends up with the parts they worked on **on their own machine** — without
|
||
having to go into the admin Gitea to fetch them — the skill drops a copy of each pushed part
|
||
folder into the **connected session folder**. At the start of a Cowork session the operator
|
||
connects a folder; that folder is the destination. If no folder was explicitly connected, use
|
||
the session's output directory `/mnt/user-data/outputs/` (create it if missing) so the copy
|
||
still lands somewhere the operator can retrieve.
|
||
|
||
The rule is: **immediately after any successful part push to Gitea**, copy that part's
|
||
`<MPN>_<make>_<typeid>/` folder — with its files intact (`<tag>.xlsx`, datasheet, `.SchLib`,
|
||
`.PcbLib`) — into the connected folder. This is automatic and never a question, exactly like
|
||
the push itself. Details and the exact command are in *Copy the pushed folder to the connected
|
||
folder* below.
|
||
|
||
Two boundaries: (1) copy **only after the push succeeds** — a failed push must not leave a
|
||
misleading copy on the operator's machine; (2) this applies to **part** pushes only
|
||
(`push-part`, and the `commit-push` used by updates, backfill, and the `.SchLib` fill-and-push).
|
||
**Skill-repo** pushes (`push-skill`, template/changelog changes) involve no part folder and are
|
||
excluded.
|
||
|
||
## Inputs
|
||
|
||
- **A datasheet PDF whose filename is the MPN** (e.g. `BAT46WJ.pdf`). If it's a series
|
||
datasheet, search that exact MPN inside to read the correct variant.
|
||
- **The make** (manufacturer), given by the user. The `make` tag is the first word of the
|
||
manufacturer, alphanumerics only (Texas Instruments → `Texas`, Nexperia → `Nexperia`).
|
||
- **Later in the flow**, after verification: an Altium **symbol** (`.SchLib`) and
|
||
**footprint** (`.PcbLib`) file, provided by the user.
|
||
|
||
## The identifier: `MPN_make_typeid`
|
||
|
||
Every part folder, every per-part workbook, and column A of every sheet (`MPN_make_type`)
|
||
use the same tag:
|
||
|
||
```
|
||
<MPN>_<make>_<typeid> e.g. BAT46WJ_Nexperia_SCH
|
||
```
|
||
|
||
`typeid` is the part's type-ID code from the taxonomy (`references/taxonomy.md`, full source
|
||
`assets/template/Type_ID.xlsx`) — Schottky → `SCH`, MOSFET → `MOS`, LDO → `LDO`. In the new
|
||
template **each typeid is its own sheet** (125 of them). The broader **Class** (Diode,
|
||
Transistor, IC …) is used only to organise the library repo into top-level folders.
|
||
|
||
## Gitea layout (two repos)
|
||
|
||
```
|
||
skill repo/ this skill's own files (updated versions land here too)
|
||
|
||
library repo/
|
||
<Class>/ e.g. Diode, IC, Transistor, Resistor, ...
|
||
<MPN>_<make>_<typeid>/ e.g. BAT46WJ_Nexperia_SCH
|
||
<MPN>_<make>_<typeid>.xlsx this part's own one-row parameter sheet
|
||
<MPN>_data.pdf the datasheet
|
||
<symbol>.SchLib user-provided
|
||
<footprint>.PcbLib user-provided
|
||
```
|
||
|
||
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
|
||
nothing.
|
||
|
||
## Who's running this (operator identity)
|
||
|
||
The whole org shares one Claude account and one Gitea token, so the skill can't tell who's
|
||
running it on its own. Instead it records the **operator's name** on everything it pushes, and
|
||
asks **only once per person**. Establish the identity at the very start of a run:
|
||
|
||
1. Read `~/.library-manager-id` on the operator's machine (via the desktop bridge):
|
||
`cat ~/.library-manager-id`.
|
||
2. **If it exists** (JSON like `{"name":"Priya Sharma","email":"priya@vecmocon.com"}`) → use it,
|
||
**don't ask**.
|
||
3. **If it does NOT exist → this person isn't onboarded yet, so ask once** (do this even though
|
||
the config carries a default, so a teammate is never silently logged as "admin"):
|
||
> "First time using the library skill on this machine — what name should your library
|
||
> changes be recorded under? (If you're the admin, just enter `admin`.) And your email,
|
||
> if you have one."
|
||
|
||
Then save it so it's remembered forever:
|
||
`echo '{"name":"<Name>","email":"<email>"}' > ~/.library-manager-id`. Every later run finds
|
||
it in step 1 and never asks again — the admin answers `admin` once, each teammate answers
|
||
their own name once, and from then on everyone is attributed correctly with no prompt.
|
||
4. **Only if you genuinely can't ask** (an unattended / scheduled run, or the machine isn't
|
||
reachable) fall back to the config `OPERATOR` default so the run isn't blocked — and say in
|
||
your summary that attribution used the default rather than a confirmed person.
|
||
|
||
Then carry that identity through the run:
|
||
|
||
- pass `--author "<Name> <<email>>"` to every `push-part` / `push-skill` / `commit-push`, and
|
||
- pass `--by "<Name>"` to `append_parameter`.
|
||
|
||
(Equivalent alternatives the scripts also read: `export LM_AUTHOR_NAME=... LM_AUTHOR_EMAIL=...`
|
||
for the session, or drop the same `~/.library-manager-id` file in the container home.)
|
||
|
||
**Identity precedence** (first one that's set wins): `--author` flag → `LM_AUTHOR_*` env →
|
||
per-person `~/.library-manager-id` → the per-install **`OPERATOR`** default in
|
||
`config/gitea.env`. So this admin install has `OPERATOR=admin`, meaning **every run/push here
|
||
is recorded as "admin" automatically**, with no file or prompt needed. A member's own
|
||
`~/.library-manager-id` (or an explicit `--author`) overrides that default with their real name.
|
||
|
||
This stamps the operator onto three things: the **commit author** (shown in `git log`, the
|
||
Gitea commit page, and `git blame`), the **commit message** (it ends with `(by <Name>)`, so the
|
||
name shows right in Gitea's activity feed), and a **By** column in the changelog. Honest limit:
|
||
the top-line "*X* pushed to main" in Gitea's activity still shows the **shared token owner** —
|
||
only giving each person their own Gitea token changes that bottom layer.
|
||
|
||
## Workflow
|
||
|
||
Run these in order. Each `python`/`bash` command is a helper in `scripts/`.
|
||
|
||
### 0. Sync the skill state from Gitea first — always
|
||
|
||
The skill's state (the template, the per-typeid versions, and the changelog) lives in the
|
||
**skill repo**, and it grows over time. A fresh install/session starts from the packaged v1
|
||
state, so if you don't sync first, a second template change wouldn't build on the first — the
|
||
versions would restart at v1 and the changelog would look like it only holds the latest change.
|
||
**So begin every run by pulling the current state:**
|
||
|
||
```bash
|
||
python scripts/gitea_components.py pull-skill
|
||
```
|
||
|
||
This copies `template.xlsx`, `versions.json`, and `CHANGELOG.xlsx` from the skill repo into the
|
||
local skill, so version bumps continue correctly (v2 → v3 → …) and the changelog stays
|
||
**cumulative from the very first change**.
|
||
|
||
### 1. Duplicate check first — before any real work
|
||
|
||
The part's presence is keyed on **MPN + make** (typeid not known yet). If it already exists,
|
||
stop; re-doing an existing part would only risk overwriting good data.
|
||
|
||
```bash
|
||
python scripts/gitea_components.py check-mpn --mpn <MPN> --make <make>
|
||
```
|
||
|
||
`ABSENT` (exit 0) → this is a **new part**; continue to step 2.
|
||
|
||
`EXISTS …` (exit 3) → the part is already in Gitea. Don't silently overwrite it, but don't
|
||
dead-end either — **ask the user whether they want to update the existing part or stop.** If
|
||
they want to revise it (new/corrected values, a swapped symbol/footprint, a newer datasheet),
|
||
go to *Updating an existing part*. If not, stop here. (If the user's request already said
|
||
"update"/"revise"/"fix" this part, take that as the answer and go straight to the update flow.)
|
||
|
||
### 2. Classify → typeid (and its Class)
|
||
|
||
Read the datasheet, identify the part, and match it to the closest subclass in
|
||
`references/taxonomy.md`; record its **typeid** (= the template sheet name). The **Class**
|
||
(for the library-repo folder) comes from the same taxonomy row —
|
||
`scripts/common.py:class_folder(typeid)` returns it (e.g. `SCH` → `Diode`).
|
||
|
||
### 3. Confirm the typeid's template (and add parameters if asked)
|
||
|
||
Check whether that typeid has a sheet in `assets/template/template.xlsx`.
|
||
|
||
- **No sheet for this typeid** → ask the user to upload the template sheet for it. Add it to
|
||
`assets/template/template.xlsx`, then push the updated skill files to the skill repo (see
|
||
*Pushing the skill repo*). Then continue.
|
||
- **Sheet exists** → print **all** of that sheet's parameters (its column headers) in the
|
||
chat and ask the user whether any new parameters should be added.
|
||
- **No** → go to step 4.
|
||
- **Yes** → collect the new parameter name(s), then:
|
||
|
||
```bash
|
||
python scripts/append_parameter.py --typeid <typeid> \
|
||
--param "New Parameter(unit)" [--param "Another(unit)"] \
|
||
--desc "why these were added" --by "<operator name>"
|
||
```
|
||
|
||
This appends the column(s) at the end of that typeid's sheet, **bumps that typeid's
|
||
Template Version and Skill Version together** (v1→v2 — see *Per-typeid versioning*),
|
||
and writes one row to the global changelog `assets/CHANGELOG.xlsx`. Then sync the updated
|
||
skill files + changelog to the skill repo with `push-skill` **automatically** (see
|
||
*Pushing the skill repo*) — that merges the new changelog row onto the one already in Gitea
|
||
rather than overwriting it.
|
||
|
||
Then ask: **should this change apply to the parts of this typeid already in Gitea?**
|
||
- **No** → go to step 4 (only the current part gets the new column).
|
||
- **Yes → backfill** (see *Backfilling existing parts*), then tell the user the previous
|
||
sheets were updated, and go to step 4.
|
||
|
||
### 4. Extract and fill the per-part workbook
|
||
|
||
Read every parameter the datasheet actually states into that typeid's columns, converting to
|
||
each header's unit. **Leave blanks where the datasheet is silent — an honest blank beats a
|
||
guess.**
|
||
|
||
The **`Description`** column is special: it is **not** free prose but a strict `_`-joined
|
||
engineering string. It **always opens with `Class_TYPEID`** — the part's library Class exactly as
|
||
spelled in its repo folder, then its Type ID in upper case — followed by the defining parameters
|
||
in a fixed order, package near the end, optional AEC-Q last. E.g.
|
||
`Resistor_FIX_36kΩ_62.2mW_±0.1%_0402`, `Capacitor_CER_2.2uF_100v_±10%_1210_x7r`,
|
||
`Diode_SCH_100V_0.25A_SOD-323F`, `Diode_TVS_5V_9.2V_18A_SOD-323`.
|
||
|
||
This `Class_TYPEID` opening is **mandatory for every part, with no exceptions** — including
|
||
Resistor, Capacitor, Zener and TVS, where it deliberately overrides the prefix given in SOP §6.
|
||
The old technology prefixes (`CHIP_RES`, `CHIP_CAP`, `TANT_CAP`, `NMOS`, `ZEN`, …) are
|
||
**retired** — never emit them. `references/description_format.md` defines the parameter order
|
||
for **every** type in the library and is authoritative; read it before writing a Description.
|
||
Look up the part's type there,
|
||
build the Description to that format, and if a real datasheet doesn't fit the format cleanly,
|
||
follow the pattern and **flag the mismatch to the engineer** rather than bending it silently.
|
||
Collect the values into a small `part.json`:
|
||
|
||
```json
|
||
{"mpn":"BAT46WJ","manufacturer":"Nexperia","typeid":"SCH",
|
||
"values":{"Description":"Diode_SCH_100V_0.25A_SOD-323F","Forward Voltage(V)":"0.71",
|
||
"Reverse Voltage(V)":"100","Forward Current(A)":"0.25","Package":"SOD-323F"}}
|
||
```
|
||
|
||
(typeid `SCH` sits in Class `Diode` → prefix `Diode_SCH`, format `Diode_SCH_Vr_Io_Package`, so a
|
||
100 V / 250 mA Schottky in SOD-323F becomes `Diode_SCH_100V_0.25A_SOD-323F`. The Class token is
|
||
the repo folder name from `CLASS_FOLDER` in `scripts/common.py`, so the Description's first token
|
||
always matches the folder the part is pushed to.)
|
||
|
||
```bash
|
||
python scripts/fill_templates.py part.json \
|
||
--template assets/template/template.xlsx --dest <stage>/<tag>/
|
||
```
|
||
|
||
This writes `<tag>.xlsx` with column A = the tag, **Skill Version (col B)** and **Template
|
||
Version (col C)** stamped from this typeid's current versions, and the four design columns
|
||
left blank for now. Every sheet also carries an **`MPN` column** (positioned just before
|
||
`Manufacturer`) that `fill_templates` populates with the part's bare MPN — it's filled from
|
||
`part["mpn"]` automatically, so no extra input is needed.
|
||
|
||
The part workbook has up to **two sheets**:
|
||
|
||
- **Sheet 1** — the typeid's parameter sheet (the one filled row).
|
||
- **Sheet 2 — `Version History`** — added **only when this typeid has had a template/skill
|
||
update**. It lists the **cumulative** change history for that typeid (Date, Skill Version,
|
||
Template Version as `v1 → v2`, Description) — every change up to the version this file was
|
||
built at. So a part built at v3 shows both `v1 → v2` and `v2 → v3`; a still-at-v1 typeid has
|
||
no second sheet. The history is read from `assets/CHANGELOG.xlsx`, so make sure the local
|
||
changelog is current (it's kept in sync by `push-skill`) before building parts.
|
||
|
||
### 5. Human verification loop
|
||
|
||
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. (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
|
||
|
||
Once verified, ask the user to upload the **symbol (`.SchLib`)** and **footprint
|
||
(`.PcbLib`)** files. Copy them into the staging part folder **under their proper names** (so
|
||
the Path columns match the files that actually get stored — strip any upload-staging prefix
|
||
the environment may have added), then read all four design values in one shot:
|
||
|
||
```bash
|
||
cp <uploaded_symbol> <stage>/<tag>/<symbol_name>.SchLib
|
||
cp <uploaded_footprint> <stage>/<tag>/<footprint_name>.PcbLib
|
||
python scripts/altium_refs.py design \
|
||
--symbol <stage>/<tag>/<symbol_name>.SchLib \
|
||
--footprint <stage>/<tag>/<footprint_name>.PcbLib > design.json
|
||
```
|
||
|
||
This produces (verified against real Ultra-Librarian exports):
|
||
|
||
- **Library Ref** = the component name **inside** the `.SchLib` (e.g. `CGA3E3X7R1H474K080AE`)
|
||
- **Library Path** = the `.SchLib` file name
|
||
- **Footprint Ref** = the **base** pattern inside the `.PcbLib`; Altium ships IPC density
|
||
variants (`-L` / `-M` / `-N`) alongside the base, and the base is the one used (e.g.
|
||
`CAP_CGA3_TDK`, not `CAP_CGA3_TDK-L`)
|
||
- **Footprint Path** = the `.PcbLib` file name
|
||
|
||
These Ref names come from *inside* the files and can differ from the MPN or filename. If a Ref
|
||
comes back `null` (or a footprint shows several unrelated candidates), ask the user to confirm
|
||
the name from Altium's properties and edit `design.json`. Then re-fill so the columns land:
|
||
|
||
```bash
|
||
python scripts/fill_templates.py part.json \
|
||
--template assets/template/template.xlsx --dest <stage>/<tag>/ --design design.json
|
||
```
|
||
|
||
### 6b. Write the mandatory symbol parameters — always, without asking
|
||
|
||
Once you have the symbol and its Library Ref, **write the mandatory symbol parameters into the
|
||
`.SchLib`**. This is **not optional and not a question** — it is a required step of every
|
||
new-part run. Do not ask "would you like me to fill the symbol parameters?", do not offer it as
|
||
a suggestion, and do not defer it to a later turn. Just do it, then report that it's done.
|
||
|
||
The SOP requires the fixed parameter set (Manufacturer, Manufacturer Part, Value, Tolerance,
|
||
Operating Temperature, RoHS, Datasheet, Process, Vecmocon Part Code, …) on every symbol, so a
|
||
part folder whose `.SchLib` has not been filled is **incomplete and must not be pushed**. Build
|
||
the part's `params.json` from the values the engineer just verified in step 5 and run:
|
||
|
||
```bash
|
||
python scripts/schlib_write.py \
|
||
--schlib <stage>/<tag>/<symbol_name>.SchLib \
|
||
--params params.json \
|
||
--out <stage>/<tag>/<symbol_name>.SchLib
|
||
```
|
||
|
||
`params.json` must carry **`description`** and **`comment`** alongside `parameters`. The
|
||
`description` is the strict `Class_TYPEID_…` string you already built for the workbook — reuse
|
||
the *exact same string* so the symbol's ComponentDescription and the Excel `Description` column
|
||
never diverge. The `comment` is the MPN. If the script warns that `description` is missing, treat
|
||
that as an error: fix `params.json` and re-run before pushing.
|
||
|
||
See *Mandatory symbol parameters* below for the full parameter set, the value-shorthand rules,
|
||
and the Ultra-Librarian default-stripping behaviour — read `references/schlib_parameters.md`
|
||
before building the parameter set. The values are already verified at this point, so this step
|
||
inherits no verification loop of its own.
|
||
|
||
### 7. Assemble the part folder
|
||
|
||
The staging folder `<tag>/` should now hold the four files: the per-part `<tag>.xlsx`, the
|
||
datasheet (name it `<MPN>_data.<ext>`), the symbol, and the footprint.
|
||
|
||
**Before pushing, check that the symbol has actually been filled** (step 6b). The part folder is
|
||
not complete until it has. If the `.SchLib` still carries only Ultra-Librarian defaults, go back
|
||
and run `schlib_write.py` — don't push, and don't ask the user whether to; just fill it.
|
||
|
||
### 8. Push to the library repo, under the part's Class
|
||
|
||
```bash
|
||
python scripts/gitea_components.py push-part --folder <stage>/<tag> --typeid <typeid> \
|
||
--author "<operator name> <<operator email>>"
|
||
```
|
||
|
||
This places the folder at `components/<Class>/<tag>/` — creating the Class folder if it
|
||
doesn't exist yet, or pushing into it if it does — and commits and pushes. Confirm to the
|
||
user where it landed. **Then copy the folder to the connected folder** (next section).
|
||
|
||
### Copy the pushed folder to the connected folder — automatic, after every part push
|
||
|
||
Right after the push above **succeeds**, copy the just-pushed `<MPN>_<make>_<typeid>/` folder
|
||
into the connected session folder so the operator has a local copy without touching Gitea. Use
|
||
the folder the operator connected at the start of the session; if none was connected, fall back
|
||
to `/mnt/user-data/outputs/`.
|
||
|
||
```bash
|
||
DEST="${CONNECTED_FOLDER:-/mnt/user-data/outputs}" # the folder connected this session
|
||
mkdir -p "$DEST/<tag>"
|
||
cp -r <stage>/<tag>/. "$DEST/<tag>/" # xlsx, datasheet, .SchLib, .PcbLib
|
||
```
|
||
|
||
Copy the folder as `<tag>/` (i.e. `<MPN>_<make>_<typeid>/`) — just the part folder, not the
|
||
`<Class>/` parent — so the connected folder simply accumulates one folder per part the operator
|
||
worked on. If that folder already exists there (a re-run or an update), refresh it so the local
|
||
copy matches what was just pushed. Then tell the user the copy is in the connected folder.
|
||
|
||
This copy step runs after **every** part push, including the `commit-push` used by *Updating an
|
||
existing part*, *Backfilling existing parts*, and the `.SchLib` *fill-and-push* — run the same
|
||
three lines against that part's folder once its push succeeds. It does **not** run for
|
||
`push-skill` (skill-repo/template/changelog pushes carry no part folder).
|
||
|
||
## Mandatory symbol parameters (.SchLib)
|
||
|
||
The SOP (§5) requires every schematic symbol to carry a fixed parameter set in its Altium
|
||
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
|
||
these parameters to this symbol", "update the schlib"), read the datasheet/specs for each part,
|
||
build its `params.json`, and run `scripts/schlib_write.py` per file — same steps as below.
|
||
|
||
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 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`,
|
||
`ROHS`, and `Process` by inference from the package.
|
||
- **Derived link** — `Datasheet` = the **Gitea URL to the pushed `<MPN>_data.<ext>`**, so the
|
||
symbol clicks straight through to the exact datasheet in the library. Build it from `GIT_HOST`
|
||
+ `LIBRARY_REPO` and the part's Class/tag
|
||
(`https://<GIT_HOST>/<LIBRARY_REPO>/raw/branch/main/<Class>/<tag>/<MPN>_data.<ext>`); the full
|
||
rule is in `references/schlib_parameters.md`.
|
||
- **Leave blank for now** — `Manufacturer 2` / `Manufacturer Part 2` (the second source). Don't
|
||
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.)
|
||
- **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`.
|
||
|
||
Collect the values into a `params.json` and write them into the symbol:
|
||
|
||
```bash
|
||
python scripts/schlib_write.py --schlib <in>.SchLib --params params.json --out <stage>/<tag>/<sym>.SchLib
|
||
```
|
||
|
||
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
|
||
`references/schlib_parameters.md` — read it before building the parameter set. If a file doesn't
|
||
round-trip, fall back to `scripts/altium_params.py` (emits an Altium DXP script to apply the same
|
||
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.
|
||
|
||
### Fill the component's Excel and push — automatically, no verification loop
|
||
|
||
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.
|
||
|
||
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:
|
||
|
||
```bash
|
||
python scripts/gitea_components.py checkout --dest work/
|
||
python scripts/gitea_components.py find-part --mpn <MPN> --make <make> --root work/ --json
|
||
python scripts/altium_refs.py design \
|
||
--symbol work/<Class>/<tag>/<sym>.SchLib \
|
||
--footprint work/<Class>/<tag>/<footprint>.PcbLib > design.json
|
||
python scripts/fill_templates.py part.json \
|
||
--template assets/template/template.xlsx --dest work/<Class>/<tag>/ --design design.json
|
||
```
|
||
|
||
(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 <stage>/<tag>/<sym>.SchLib work/<Class>/<tag>/<sym>.SchLib
|
||
python scripts/gitea_components.py commit-push --root work/ \
|
||
--message "update <tag>: fill parameters + symbol (by <operator name>)" \
|
||
--author "<operator name> <<operator email>>"
|
||
```
|
||
|
||
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.) **Then copy the
|
||
part folder to the connected folder** — see *Copy the pushed folder to the connected folder*.
|
||
|
||
## Per-typeid versioning
|
||
|
||
Versioning is **per typeid**, not global. Each typeid carries its own `template_version` and
|
||
`skill_version` in `assets/template/versions.json` (both start at 1). When a parameter is
|
||
added to a typeid, that typeid gets a new template, so its `template_version` bumps — and on
|
||
the back of that its `skill_version` bumps too (v1→v2). **Only that typeid moves**; every
|
||
other typeid keeps its versions. Those two numbers are exactly what `fill_templates` stamps
|
||
into that typeid's rows (cols B and C), so a row always records the template/skill version it
|
||
was built against. `append_parameter.py` does the bump; `common.py` is the single source for
|
||
reading and writing these numbers.
|
||
|
||
**One deliberate exception already applied:** the `MPN` column (before `Manufacturer`) was
|
||
added to **every** typeid sheet as a one-off structural change **without** bumping any
|
||
`template_version`/`skill_version` and **without** a changelog row — it's a bookkeeping column,
|
||
not a spec parameter, and versions were intentionally left where they were. Don't treat the
|
||
existing sheets' MPN column as a version discrepancy to "correct." (This no-bump behaviour is
|
||
specific to that one change; genuine parameter additions still go through `append_parameter.py`
|
||
and bump normally.)
|
||
|
||
## The changelog
|
||
|
||
`append_parameter.py` maintains one **global** changelog as an Excel workbook at
|
||
`assets/CHANGELOG.xlsx` (sheet `Changelog`, styled green header). Every time a
|
||
typeid's template/version changes, one row is appended with columns
|
||
**Date | Typeid | Skill Version | Template Version | Description** — the version columns hold
|
||
the new versions, and Description is your note (or the parameter(s) added if you gave none).
|
||
|
||
The changelog lives in the **skill repo in Gitea** as well, and it is **cumulative from the
|
||
first change onward**. Two things keep it that way: at the start of a run `pull-skill` (step 0)
|
||
brings the current changelog down so a new change appends to the full history, and on push
|
||
`push-skill` **merges** the new local rows onto the changelog already in Gitea — appended,
|
||
never overwritten. So the Gitea copy is the growing, authoritative history across machines and
|
||
sessions; the merged file is copied back locally so the two stay in sync. If you ever see the
|
||
Gitea changelog with only the latest change, it means step 0 (`pull-skill`) was skipped.
|
||
|
||
## Backfilling existing parts
|
||
|
||
When the user wants a newly-added parameter applied to parts of that typeid already in Gitea:
|
||
|
||
```bash
|
||
python scripts/gitea_components.py checkout --dest work/
|
||
python scripts/gitea_components.py list-type --typeid <typeid> --root work/ --json
|
||
```
|
||
|
||
`list-type` lists every existing part of that typeid with the files in its folder — including
|
||
its datasheet, which is co-located. For each one: read that datasheet, re-extract the values
|
||
(including the new parameter), and rebuild its per-part sheet in place:
|
||
|
||
```bash
|
||
python scripts/fill_templates.py <that_part>.json \
|
||
--template assets/template/template.xlsx --dest work/<Class>/<that_tag>/
|
||
```
|
||
|
||
Because `fill_templates` uses the current template and current versions, each rebuilt sheet
|
||
picks up the new column and the bumped version automatically. When all are done, push once
|
||
and tell the user the previous sheets were updated:
|
||
|
||
```bash
|
||
python scripts/gitea_components.py commit-push --root work/ --message "backfill <param> into <typeid>"
|
||
```
|
||
|
||
Then copy each rebuilt part folder into the connected folder (see *Copy the pushed folder to
|
||
the connected folder*) so the operator's local copies reflect the backfill too.
|
||
|
||
## Updating an existing part
|
||
|
||
When the duplicate check finds the part (or the user asks to revise one that's already in
|
||
Gitea), you're **editing a part in place** rather than assembling a new one. The mechanics are
|
||
the same edit-in-a-checkout pattern as backfill — the difference is you're changing one part's
|
||
own data, not applying a template change across a whole typeid. The guiding rule doesn't
|
||
change: nothing is overwritten until the engineer has verified the new version.
|
||
|
||
1. **Locate the part in a checkout you can commit.** Clone the library repo, then find the
|
||
part's folder and typeid (recovered from its tag):
|
||
|
||
```bash
|
||
python scripts/gitea_components.py checkout --dest work/
|
||
python scripts/gitea_components.py find-part --mpn <MPN> --make <make> --root work/ --json
|
||
```
|
||
|
||
`find-part` returns the part's Class, tag, typeid, the editable folder path under `work/`,
|
||
and the files in it (its current `<tag>.xlsx`, datasheet, symbol, footprint). If it prints
|
||
`NOT FOUND` (exit 4), the part isn't actually there — treat it as a **new** part and go back
|
||
to the normal add flow from step 2.
|
||
|
||
2. **Decide with the user what's changing.** A part update can revise any of: the **parameter
|
||
values** (re-read the datasheet, or a corrected/newer one), the **symbol/footprint**, or the
|
||
**datasheet PDF** itself. Ask which, so you only touch what's meant to change and leave the
|
||
rest of the folder intact.
|
||
|
||
3. **Apply the change in place, in `work/<Class>/<tag>/`.** A key thing to understand first:
|
||
`fill_templates` **rewrites the whole data row**, so it fills the four design columns
|
||
(`Library Ref/Path`, `Footprint Ref/Path`) from the `--design` map you give it — and leaves
|
||
them **blank if you don't**. When you rebuild a sheet, always re-supply the design values, or
|
||
you'll silently wipe the symbol/footprint refs that were already there. The symbol and
|
||
footprint files live in the part folder, so re-deriving them is cheap: point `altium_refs`
|
||
at whatever the folder will hold **after** your change.
|
||
|
||
- **Values** (re-read the datasheet, or a corrected/newer one) → read the current
|
||
`<tag>.xlsx` and the folder's datasheet so you start from what's there, re-extract into a
|
||
fresh `part.json` (Description still built to `references/description_format.md`),
|
||
re-derive the design columns from the folder's existing symbol/footprint, and rebuild the
|
||
sheet in place **with `--design`**:
|
||
|
||
```bash
|
||
python scripts/altium_refs.py design \
|
||
--symbol work/<Class>/<tag>/<symbol>.SchLib \
|
||
--footprint work/<Class>/<tag>/<footprint>.PcbLib > design.json
|
||
python scripts/fill_templates.py part.json \
|
||
--template assets/template/template.xlsx --dest work/<Class>/<tag>/ --design design.json
|
||
```
|
||
|
||
Because `fill_templates` reads the **current** template and versions, the rebuilt sheet
|
||
keeps this typeid's version stamp (a part-data fix isn't a template change, so nothing
|
||
bumps) and picks up any columns the typeid has gained — while the `--design` map carries
|
||
the existing symbol/footprint refs through unchanged. (If a part somehow has no
|
||
symbol/footprint yet, there's nothing to preserve — rebuild without `--design`.)
|
||
- **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 misma |