component
GRM155R71H103KA88D - Murata
Public Made by Adomby adom
MLCC 10 nF +/-10 %, 50 V, X7R, 0402 (1005 metric)
main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
"""Add the adom-hero 1.4 hero GLB and the Altium SchLib/PcbLib to an existing component page clone,
and refresh page.json, models.json, verification.json, provenance.json, README.md and LICENSE for them.
Everything else in the clone is left as it is.
usage: upgrade.py <clone-dir>
"""
import sys, os, json, hashlib, subprocess, shutil, re
TOOLS = os.path.dirname(os.path.abspath(__file__))
PY = "/home/adom/.venvs/cq/bin/python"
TODAY = "2026-10-21"
D = os.path.abspath(sys.argv[1]); slug = os.path.basename(D)
def p_(f): return os.path.join(D, f)
sha = lambda f: hashlib.sha256(open(p_(f), "rb").read()).hexdigest()
def blob(f):
b = open(p_(f), "rb").read(); return hashlib.sha1(b"blob %d\0" % len(b) + b).hexdigest()
def run(cmd, cwd=None):
r = subprocess.run(cmd, capture_output=True, text=True, cwd=cwd)
if r.returncode != 0:
print("FAILED", cmd, r.stdout[-1500:], r.stderr[-1500:]); sys.exit(1)
return r
page = json.load(open(p_("page.json"))); comp = page["component"]; parts = comp["parts"]; MPN = comp["mpn"]
SYM, FP, STEP = parts["symbol"], parts["footprint"], parts["step"]
PLAIN = "package-reference.glb"
assert parts.get("model_3d") == PLAIN and os.path.exists(p_(PLAIN)), parts
HERO = f"{slug}-hero.glb"; HERO_SC = f"{slug}-hero.overlay.json"; PREV = f"docs/{slug}-hero-preview.png"
by = {"name": "Claude (AI agent) for John Lauer", "handle": "john"}
# hero
r = run([PY, "-I", os.path.join(TOOLS, "hero_glb.py"), p_(PLAIN), p_(FP), p_("page.json"), p_(HERO), "--png", p_(PREV)], cwd=TOOLS)
hero_line = r.stdout.strip().splitlines()[-1]; print("hero:", hero_line)
assert "align OK" in hero_line and os.path.exists(p_(HERO_SC))
hero_res = hero_line.split(": ", 1)[-1]
# altium
os.makedirs(p_("altium"), exist_ok=True)
run(["adom-lbr", "import-kicad", "--sym", p_(SYM), "--fp", p_(FP), "--mpn", MPN], cwd=p_("altium"))
run(["adom-lbr", "export-altium", f"{MPN}.adom-lbr.json", "--out-dir", "."], cwd=p_("altium"))
ALT = [f"altium/{MPN}.SchLib", f"altium/{MPN}.PcbLib", f"altium/{MPN}.adom-lbr.json"]
assert all(os.path.getsize(p_(f)) > 1000 for f in ALT)
# tools into provenance
for t in ("hero_glb.py", "hero_glyphs.json", "upgrade.py"):
shutil.copyfile(os.path.join(TOOLS, t), p_("provenance/tools/" + t))
# page.json
parts["model_3d"] = HERO; parts["model_3d_plain"] = PLAIN
parts["altium_schlib"], parts["altium_pcblib"] = ALT[0], ALT[1]
page.setdefault("eda_assets", {})["altium"] = {"schlib": ALT[0], "pcblib": ALT[1], "provenance": "generated"}
comp.setdefault("provenance", {})["hero"] = "adom-hero 1.4 (adom/component-hero hero_glb.py)"
json.dump(page, open(p_("page.json"), "w"), indent=1)
# models.json
mj = json.load(open(p_("models.json")))
src = next(m for m in mj["models"] if m["path"] == PLAIN)
src.update({"slot": "model_3d_plain", "use": sorted(set(src.get("use", []) + ["board-insertion"])),
"unique": "The clean model as a GLB (step2glb), no artwork: the download for viewers and assemblies"})
mj["models"] = [m for m in mj["models"] if m["path"] != HERO]
i = mj["models"].index(src) + 1
mj["models"].insert(i, {"path": HERO, "format": "glb", "title": "Hero (page viewer)", "status": "current", "slot": "model_3d", "variant": "hero",
"role": "derived", "use": ["wiki-hero"], "marking": None, "derived_from": PLAIN,
"unique": "The clean model plus nonphysical footprint artwork (adom-hero 1.4): teal dashed pad outlines, silkscreen, pad numbers 1 and 2; not for board use",
"thumb": PREV, "source": src.get("source"), "sha256": sha(HERO), "git_blob": blob(HERO), "bytes": os.path.getsize(p_(HERO))})
mj["updated"] = TODAY
json.dump(mj, open(p_("models.json"), "w"), indent=1)
# verification.json (append)
vj = json.load(open(p_("verification.json")))
vj["entries"] = [e for e in vj["entries"] if e.get("title") not in ("Hero GLB built to adom-hero 1.4 over the clean model", "Altium libraries generated from the KiCad symbol and footprint")]
vj["entries"].append({"date": TODAY, "by": by, "status": "verified", "title": "Hero GLB built to adom-hero 1.4 over the clean model",
"scope": ["3d-model", "hero"], "variants": ["*"],
"checks": [{"text": hero_res, "result": "pass"},
{"text": "pad outlines teal 50 %, silkscreen 30 % alpha, pad-number labels 50 % (the terminals have no names); no pin-1 dot, the part is non-polar", "result": "pass"},
{"text": "hero is a separate file; package-reference.glb and every LOD carry no artwork", "result": "pass"},
{"text": "preview looked at by the AI agent; the live wiki viewer still needs a person's look", "result": "note"}],
"evidence": [{"label": "preview (top and oblique)", "href": PREV}, {"label": "sidecar", "href": HERO_SC}],
"files": {HERO: sha(HERO), PLAIN: sha(PLAIN), FP: sha(FP)}})
vj["entries"].append({"date": TODAY, "by": by, "status": "verified", "title": "Altium libraries generated from the KiCad symbol and footprint",
"scope": ["symbol", "footprint", "eda"], "variants": ["*"],
"checks": [{"text": "adom-lbr import-kicad + export-altium: native .SchLib (2 pins) and .PcbLib (2 pads) written and read back; MPN, Manufacturer and supplier numbers carried as parameters", "result": "pass"},
{"text": "not yet opened in Altium by a person", "result": "note"}],
"evidence": [], "files": {f: sha(f) for f in ALT}})
json.dump(vj, open(p_("verification.json"), "w"), indent=1)
# README (additions only)
rd = open(p_("README.md")).read()
if HERO not in rd:
hero_txt = (f"The page viewer shows `{HERO}`: the clean model plus nonphysical reference artwork drawn under it to the adom-hero 1.4\n"
"standard (adom/component-hero): the footprint's pads as teal dashed outlines at 50 % opacity, its silkscreen at\n"
"70 % transparency and the pad numbers 1 and 2 (the terminals have no names). The part is non-polar, so no pin-1 dot is\n"
f"added. The artwork never goes into the STEP, `{PLAIN}` or the board LODs.\n\n"
f"\n\n")
k = rd.index("::adom-models")
rd = rd[:k] + hero_txt + rd[k:]
row = next(l for l in rd.splitlines() if l.startswith(f"| `{PLAIN}` |"))
new = (row + "\n" + f"| `{HERO}` | page-viewer hero: the clean model plus footprint artwork (adom-hero 1.4); `{HERO_SC}` lists every pad and label |\n"
f"| `{ALT[0]}` | Altium schematic library, made with `adom-lbr import-kicad` and `export-altium` |\n"
f"| `{ALT[1]}` | Altium PCB library, made with `adom-lbr import-kicad` and `export-altium` |\n"
f"| `{ALT[2]}` | adom-lbr hub JSON (symbol, footprint, pin-to-pad map) behind the Altium files |")
rd = rd.replace(row, new, 1)
open(p_("README.md"), "w").write(rd)
# LICENSE (one wording change so the hero is covered)
lic = open(p_("LICENSE")).read()
lic2 = lic.replace("(and the GLB and LOD files derived from it)", "(and the GLB, hero and LOD files derived from it)")
if "component-hero" not in lic2:
lic2 = lic2.rstrip("\n") + "\n- provenance/tools/hero_glb.py and hero_glyphs.json come from adom/component-hero.\n"
open(p_("LICENSE"), "w").write(lic2)
# provenance.json (add sections, refresh every file hash)
pv = json.load(open(p_("provenance.json")))
pv["hero_glb"] = {"file": HERO, "sidecar": HERO_SC, "preview": PREV, "added": TODAY,
"tool": "hero_glb.py (adom/component-hero, adom-hero 1.4), copied into provenance/tools/",
"inputs": [PLAIN, FP, "page.json component.pins (1 and 2, unnamed)"], "result": hero_res}
pv["altium"] = {"files": ALT, "added": TODAY, "tool": "adom-lbr import-kicad --sym --fp --mpn, then adom-lbr export-altium (adom-lbr 2.47.5)"}
pv["upgrade_note"] = f"{TODAY}: hero GLB, Altium libraries and Source added by provenance/tools/upgrade.py; no other file changed except the records that describe them (page.json, models.json, verification.json, README.md, LICENSE, this file)."
files = {}
for dp, dn, fn in os.walk(D):
for f in fn:
rel = os.path.relpath(os.path.join(dp, f), D)
if rel != "provenance.json":
files[rel] = sha(rel)
pv["files"] = files
json.dump(pv, open(p_("provenance.json"), "w"), indent=1)
print("upgraded", slug)