component
GRM155R71H472KA01D - Murata
Public Made by Adomby adom
MLCC 4.7 nF +/-10 %, 50 V, X7R, 0402 (1005 metric)
main
John Lauer
Component record for grm155r71h472ka01d: 16:10 hero image and refreshed records
ad19879
2d ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
"""Write README.md, land-pattern/README.md, page.json, package.json, models.json,
provenance.json, verification.json and LICENSE for one built page.
usage: records.py <slug>
"""
import sys, os, json, hashlib, re
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from parts import PARTS, TODAY
ROOT = "/tmp/claude-1001/mlcc5"
slug = sys.argv[1]
P = next(p for p in PARTS if p["slug"] == slug)
OUT = os.path.join(ROOT, "pages", slug)
MPN = P["mpn"]; OWNER = "adom"
B = json.load(open(os.path.join(ROOT, "work", f"{slug}-build.json")))
STEP, FP, SYM = B["step"], B["fp"], B["sym"]
check, land = B["check"], json.load(open(os.path.join(OUT, "land-pattern/land-check.json")))
lod = json.load(open(os.path.join(OUT, "lod/manifest.json")))
def p_(f): return os.path.join(OUT, 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()
D = P["dist"]
HERO = B["hero_glb"]; HERO_SC = HERO.replace(".glb", ".overlay.json")
FIGS = B["figures"]
ALT = [f for f in (f"altium/{MPN}.SchLib", f"altium/{MPN}.PcbLib", f"altium/{MPN}.adom-lbr.json") if os.path.exists(os.path.join(OUT, f))]
LBR_OK = os.path.exists(os.path.join(OUT, f"{MPN}.lbr"))
MSTEP_NOTE = (f"Murata's public product-data API (pimapi.murata.com/public/api/pim/v1/products/search, queried {TODAY} for {MPN[:-1]}#) "
"lists the reference sheet, reliability data, S-parameters, SPICE netlist and characteristics data for this part but no STEP or other 3D CAD asset; "
"the product page (pim.murata.com) is a script-built page with no static CAD link, and guessed static paths returned 404. "
"No browser session was used, so KiCad's library model was used and checked against Murata's printed dimensions.")
by = {"name": "Claude (AI agent) for John Lauer", "handle": "john"}
KICAD_SRC = f"KiCad packages3D {P['fp_lib']}.3dshapes/{P['fp']}.step (CC-BY-SA 4.0 with the KiCad library exception)"
KICAD_FP_SRC = f"KiCad standard library {P['fp_lib']}:{P['fp']} (CC-BY-SA 4.0 with the KiCad library exception)"
GL = "https://gitlab.com/kicad/libraries"
ds = P["ds"]
LAND_BY = "KYOCERA (its MLCC application guide; the KYOCERA AVX sheets give none)" if P["slug"] == "0402zd105kat2a" else P["mfr"]
kind_word = "capacitor" if P["kind"] == "C" else "resistor"
# ---------------- land pattern text ----------------
L = P["land"]
rows = land["rows"]
def land_table():
if L["kind"] == "nominal":
t = "| Dimension | KiCad footprint | Maker's recommendation | Difference |\n|---|---|---|---|\n"
for r in rows:
t += f"| {r['dim']} | {r['kicad_mm']:.2f} mm | {r['maker_nominal']:.2f} mm | {r['difference_mm']:+.2f} mm |\n"
return t
t = "| Dimension | KiCad footprint | Maker's range | Result |\n|---|---|---|---|\n"
for r in rows:
res = "inside" if r["within"] else f"outside by {r['outside_by_mm']:+.2f} mm"
t += f"| {r['dim']} | {r['kicad_mm']:.2f} mm | {r['maker_min']:.2f} to {r['maker_max']:.2f} mm | {res} |\n"
return t
outs = [r for r in rows if r.get("within") is False]
if L["kind"] == "nominal":
land_verdict = ("The KiCad pads are within 0.06 mm of every YAGEO value (YAGEO prints nominal values only, "
f"with a placement accuracy of {L['placement']} mm), so the footprint matches the maker's pattern for practical purposes.")
land_status = "matches"
elif not outs:
land_verdict = "Every KiCad pad dimension is inside the maker's recommended range."
land_status = "inside"
else:
o = ", ".join(f"{r['dim']} by {r['outside_by_mm']:+.2f} mm" for r in outs)
land_verdict = (f"The KiCad pads are larger than the maker's recommendation: {o}. The gap between the pads is inside the range. "
"KiCad's footprint is generated to IPC-7351 nominal density (its own description says so), not to this maker's table. "
"A design that wants the maker's land should shrink the pads to the numbers in the table.")
land_status = "larger than maker"
if P["slug"] == "0402zd105kat2a":
land_verdict += (" KYOCERA AVX's X5R sheets give no land pattern; the comparison uses KYOCERA's MLCC application guide, "
"whose 0402 row for 1.0 +/-0.20 mm parts (this body) is left blank, so the nearest printed row (1.0 +/-0.15 mm) is used.")
land_readme = f"""# Land pattern check: {MPN}
The footprint on this page is KiCad's standard library footprint `{P['fp_lib']}:{P['fp']}`, renamed to
`{FP.replace('.kicad_mod','')}` so it can sit beside other parts in one library folder. The copper is unchanged.
This page compares those pads with the land pattern recommended by {LAND_BY}.

Grey is the part body at its datasheet nominal size, teal is the KiCad copper, the dashed amber outline is the maker's
land (middle of its printed range, or its printed nominal), and the amber bands show where the maker allows the inner
and outer pad edges when it prints a range.
## Numbers
{land_table()}
Maker's letters: {L['letters']}.
Source: {L['source']}.{(' Link: ' + L['url']) if L.get('url') else ''}

## Result
{land_verdict}
KiCad pad centres are at +/-{abs(land['kicad']['overall'] - land['kicad']['len']) / 2:.2f} mm, pad size {land['kicad']['len']:.2f} x {land['kicad']['wid']:.2f} mm,
rounded rectangle (corner ratio 0.25). The numbers are in [land-check.json](land-check.json); the drawing is made by
`provenance/tools/landcheck.py` from the footprint file and the maker's table.
"""
open(p_("land-pattern/README.md"), "w").write(land_readme)
# ---------------- verification.json ----------------
entries = []
ck = [{"text": f"{c['dim']}: model {c['model_mm']:.3f} mm, datasheet {c['datasheet_min']:.2f} to {c['datasheet_max']:.2f} mm", "result": "pass" if c["pass"] else "fail"} for c in check["checks"]]
ck.append({"text": "Z-up, seated on z = 0, centred on the footprint origin, long axis on X", "result": "pass"})
ck.append({"text": "no Murata STEP was reachable by plain HTTP for this exact part number (public product API lists no 3D asset); the model is KiCad's library package, checked against Murata's printed L, W, T and e limits", "result": "note"})
if P["slug"] == "0402zd105kat2a":
ck.append({"text": "the sheet prints thickness as a maximum only (letter C, 0.56 mm); the check range is 0 to 0.56 mm", "result": "note"})
entries.append({"date": TODAY, "by": by, "status": "verified", "title": "3D model matches the datasheet package dimensions",
"scope": ["3d-model"], "variants": ["*"], "checks": ck,
"evidence": [{"label": "overlay (top, bottom, side, end at true scale)", "href": f"docs/verify/{slug}-overlay.png"},
{"label": "numbers", "href": f"docs/verify/{slug}-check.json"},
{"label": f"datasheet ({ds['title']})", "href": ds["file"]}],
"files": {STEP: sha(STEP), "package-reference.glb": sha("package-reference.glb"), ds["file"]: sha(ds["file"])}})
lck = []
for r in rows:
if "within" in r:
lck.append({"text": f"{r['dim']}: KiCad {r['kicad_mm']:.2f} mm, maker {r['maker_min']:.2f} to {r['maker_max']:.2f} mm", "result": "pass" if r["within"] else "note"})
else:
lck.append({"text": f"{r['dim']}: KiCad {r['kicad_mm']:.2f} mm, maker {r['maker_nominal']:.2f} mm (difference {r['difference_mm']:+.2f} mm)", "result": "pass"})
entries.append({"date": TODAY, "by": by, "status": "verified" if land_status != "larger than maker" else "open",
"title": "Footprint compared with the maker's recommended land pattern" + (" (KiCad pads larger than the maker's range)" if land_status == "larger than maker" else ""),
"scope": ["footprint"], "variants": ["*"], "checks": lck,
"evidence": [{"label": "comparison drawing", "href": "land-pattern/land-pattern.png"}, {"label": "numbers", "href": "land-pattern/land-check.json"},
{"label": "write-up", "href": "land-pattern/README.md"}],
"files": {FP: sha(FP)}})
entries.append({"date": TODAY, "by": by, "status": "verified", "title": "Marking: the model shows the real part, which is unmarked",
"scope": ["3d-model", "marking"], "variants": ["*"],
"checks": [{"text": f"{P['marking']} ({P['marking_src']})", "result": "pass"},
{"text": "no AI-added part-number etch on any model", "result": "pass"}],
"evidence": [{"label": "datasheet", "href": ds["file"]}], "files": {STEP: sha(STEP)}})
entries.append({"date": TODAY, "by": by, "status": "verified", "title": "Board-use LOD GLBs are clean and share the STEP pose",
"scope": ["lod"], "variants": ["*"],
"checks": [{"text": "four levels written, read back, bounds equal the STEP (Z-up, metres, seat z = 0)", "result": "pass"},
{"text": "no footprint, pad outline, label or silkscreen helpers; terminals present at every level", "result": "pass"},
{"text": "previews looked at by the AI agent, not yet by a person", "result": "note"}],
"evidence": [{"label": "manifest", "href": "lod/manifest.json"}],
"files": {lv["path"]: lv["sha256"] for lv in lod["levels"]}})
entries.append({"date": TODAY, "by": by, "status": "verified", "title": "Pin 1 and polarity",
"scope": ["footprint", "3d-model", "symbol"], "variants": ["*"],
"checks": [{"text": f"non-polar two-terminal {kind_word}: pads 1 and 2 are interchangeable; symbol pins 1 and 2 map to pads 1 and 2", "result": "pass"},
{"text": "model terminals sit on the footprint pads (both centred on the origin, long axis on X)", "result": "pass"}],
"evidence": [], "files": {FP: sha(FP), SYM: sha(SYM), STEP: sha(STEP)}})
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": B["hero_line"].split(": ", 1)[-1], "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": f"docs/{slug}-hero-preview.png"}, {"label": "sidecar", "href": HERO_SC}],
"files": {HERO: sha(HERO), "package-reference.glb": sha("package-reference.glb"), FP: sha(FP)}})
if ALT or LBR_OK:
entries.append({"date": TODAY, "by": by, "status": "verified", "title": "Fusion and Altium libraries generated from the KiCad symbol and footprint",
"scope": ["symbol", "footprint", "eda"], "variants": ["*"],
"checks": [{"text": "adom-lbr generate: .lbr lint passed (0 errors)", "result": "pass"},
{"text": "adom-lbr import-kicad + export-altium: native .SchLib and .PcbLib written; MPN, Manufacturer and supplier numbers carried as parameters", "result": "pass"},
{"text": "not yet opened in Fusion 360 or Altium by a person", "result": "note"}],
"evidence": [], "files": {f: sha(f) for f in ([f"{MPN}.lbr"] if LBR_OK else []) + ALT}})
ver = {"schema": "adom/verification@1", "part": MPN, "variants": [], "entries": entries}
json.dump(ver, open(p_("verification.json"), "w"), indent=1)
# ---------------- models.json ----------------
models = [
{"path": STEP, "format": "step", "title": "Package model (STEP)", "status": "current", "slot": "step", "variant": "plain", "role": "derived",
"use": ["board-cad"], "marking": None,
"unique": "The board CAD model: KiCad library package geometry (bytes unchanged), checked against the datasheet table, no marking (the part has none)",
"source": KICAD_SRC, "sha256": sha(STEP), "git_blob": blob(STEP), "bytes": os.path.getsize(p_(STEP)), "thumb": "docs/models/package-reference-step.png"},
{"path": "package-reference.glb", "format": "glb", "title": "Package model (GLB)", "status": "current", "slot": "model_3d_plain", "variant": "plain", "role": "derived",
"use": ["render", "board-insertion"], "marking": None, "derived_from": STEP, "unique": "The clean model as a GLB (step2glb), no artwork: the download for viewers and assemblies",
"source": KICAD_SRC, "sha256": sha("package-reference.glb"), "git_blob": blob("package-reference.glb"), "bytes": os.path.getsize(p_("package-reference.glb"))},
]
models.append({"path": HERO, "format": "glb", "title": "Hero (page viewer)", "status": "current", "slot": "model_3d", "variant": "hero", "role": "derived",
"use": ["wiki-hero"], "marking": None, "derived_from": "package-reference.glb",
"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": f"docs/{slug}-hero-preview.png", "source": KICAD_SRC, "sha256": sha(HERO), "git_blob": blob(HERO), "bytes": os.path.getsize(p_(HERO))})
for lv in lod["levels"]:
models.append({"path": lv["path"], "format": "glb", "title": f"Board LOD, {lv['level']}", "status": "current", "variant": "lod", "role": "derived",
"use": ["board-insertion", "render"], "marking": None, "derived_from": STEP,
"unique": f"{lv['level']} detail for board and scaffold viewers: {lv['triangles']} triangles, {lv['primitives']} primitives",
"thumb": lv["preview"]["path"], "source": KICAD_SRC, "sha256": lv["sha256"], "git_blob": blob(lv["path"]), "bytes": lv["sizeBytes"]})
json.dump({"schema": "adom/models@1", "part": MPN, "models": models, "updated": TODAY}, open(p_("models.json"), "w"), indent=1)
# ---------------- LICENSE ----------------
open(p_("LICENSE"), "w").write(f"""{MPN} component page
Original Adom content on this page (README text, the KiCad symbol {SYM}, the
verification drawings, the hero artwork, the Fusion and Altium libraries generated\nfrom them, the JSON records and the scripts in provenance/tools/; hero_glb.py and\nhero_glyphs.json come from adom/component-hero)
is licensed under the Creative Commons Attribution 4.0 International License
(CC-BY-4.0): https://creativecommons.org/licenses/by/4.0/
Third-party files keep their own terms and are not relicensed by this page:
- {FP} and {STEP} (and the GLB, hero and LOD files derived from it) are KiCad library
content, CC-BY-SA 4.0 with the KiCad library exception
({GL}/kicad-footprints, {GL}/kicad-packages3D).
- The PDFs in docs/ are {P['mfr']} documents, copyright of their publishers,
included unchanged for reference; the PNG crops in docs/figures/ are excerpts of
those PDFs, shown as citations.
""")
# ---------------- README ----------------
spec_tbl = "| Parameter | Value | Datasheet |\n|---|---|---|\n" + "".join(f"| {a} | {b} | {c} |\n" for a, b, c in P["spec_rows"])
res_rows = [("Product page", P["product_url"]), ("Datasheet", f"{ds['title']}, [PDF]({ds['file']}) ([source]({ds['url']}))")]
for x in P["extra_docs"]:
res_rows.append(("Supporting document", f"{x['title']}, [PDF]({x['file']}) ([source]({x['url']}))"))
if L.get("url"):
res_rows.append(("Land pattern guidance", f"[{L['source'].split(',')[0]}]({L['url']})"))
res_tbl = "| Resource | Link |\n|---|---|\n" + "".join(f"| {a} | {b} |\n" for a, b in res_rows)
dist_tbl = ("| Distributor | Part number | Stock seen " + TODAY + " |\n|---|---|---|\n"
+ "".join(f"| {n} | [{D[k][0]}]({D[k][1]}) | {D[k][2]:,} |\n" for k, n in (("mouser", "Mouser"), ("digikey", "DigiKey"), ("lcsc", "LCSC / JLCPCB"))))
notes = "".join(f"- {n}\n" for n in P["notes"])
fail_bits = ", ".join(f"{c['dim']} {c['model_mm']:.3f} mm (datasheet {c['datasheet_min']:.2f} to {c['datasheet_max']:.2f})" for c in check["checks"])
card_three = (f"The footprint is KiCad's standard `{P['fp']}`, and this guide lays its pads over the land pattern recommended by {LAND_BY}, at true scale. "
f"{'It matches the maker closely.' if land_status in ('matches', 'inside') else 'The KiCad pads are a little larger than the maker asks for, and the guide shows by how much.'} "
f"Use it to decide whether to keep the KiCad pads or shrink them to the maker's numbers before layout.")
readme = f"""# {MPN}
{P['mfr']} {MPN}: {P['brief']}.
::adom-verified{{src="/{OWNER}/{slug}/render/verification.json"}}
| Field | Value |
|---|---|
| Manufacturer | {P['mfr_full']} |
| Manufacturer part number | {MPN} |
| Type | {P['subcategory']} |
| Value | {P['value']} |
| Tolerance | {P['tol']} |
| {'Rated voltage' if P['kind'] == 'C' else 'Voltage'} | {P['voltage']} |
| {P['rating_row'][0]} | {P['rating_row'][1]} |
| Operating temperature | {P['op_temp']} |
| Package | {P['package']} |
| Datasheet | [{ds['title']}]({ds['file']}) |
## Key specifications
{spec_tbl}
All page numbers refer to the PDF in [docs/]({ds['file']}) unless stated otherwise.
## Datasheet pages cited
Crops of the reference sheet pages the tables above cite, rendered at 150 dpi from the PDF in docs/.
{chr(10).join(f"**{f['caption']}**{chr(10)}{chr(10)}![{f['caption']}]({f['file']}){chr(10)}" for f in FIGS)}
{('## Notes' + chr(10) + chr(10) + notes) if notes else ''}
## Ordering
{dist_tbl}
Stock figures are a snapshot from the Adom parts search on {TODAY}; check the distributor before ordering.
## Manufacturer and resources
{res_tbl}
## Pins
| Pin | Function |
|---|---|
| 1 | terminal (non-polar) |
| 2 | terminal (non-polar) |
A {kind_word} of this kind has no polarity and no pin 1 orientation: either terminal may face either way.
## Land pattern: KiCad footprint vs {P['mfr']}
<table>
<tr>
<td width="104"><a href="land-pattern/README.md"><img src="land-pattern/land-pattern-icon.png" width="112" alt="Land pattern"></a><br><sub> Land pattern </sub></td>
<td>{card_three}<br><br><a href="land-pattern/README.md">The comparison, with the numbers</a></td>
</tr>
</table>
## 3D model
Murata publishes no STEP for this exact part number that can be fetched over plain HTTP: its public product-data API
lists the reference sheet, S-parameters, a SPICE netlist and characteristics data for this part, but no 3D CAD file
(checked {TODAY}). The page therefore uses KiCad's library model for the package, `{P['fp']}.step`, with its bytes
unchanged, renamed `{STEP}`. It was checked against Murata's printed L, W, T and e limits before use (next section).
`package-reference.glb` is the same model converted with `step2glb convert`, with nothing added. {P['marking']}
(searched: {P['marking_src']}), so the model shows a plain body and carries no added part-number etch.
The page viewer shows `{HERO}`: the clean model plus nonphysical reference artwork drawn under it to the adom-hero 1.4
standard (adom/component-hero): the footprint's pads as teal dashed outlines at 50 % opacity, its silkscreen at
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
added. The artwork never goes into the STEP, `package-reference.glb` or the board LODs.

::adom-models{{src="/{OWNER}/{slug}/render/models.json"}}
## Model checked against the datasheet
The model was rendered in orthographic top, bottom, side and end views at true scale and laid over a drawing made at the
same scale from the printed table (nominal outline, tolerance limits, allowed terminal band). Every dimension is inside
the printed range: {fail_bits}. The model is Z-up, seated on z = 0, centred on the footprint origin, long axis on X.

Source: {ds['title']}, {P['dims_text']}.
## Board levels of detail
Clean board-use GLBs (no footprint pads, outlines, labels or silkscreen; no marking) in four levels, all in the same
Z-up, metre, footprint-origin pose as the STEP. Previews are rendered from each GLB.
::adom-component-lod{{src="/{OWNER}/{slug}/render/lod/manifest.json"}}
## CAD files
| File | What it is |
|---|---|
| `{SYM}` | KiCad symbol (standard {kind_word} graphic) with MPN, Manufacturer, Mouser, DigiKey and LCSC fields |
| `{FP}` | KiCad footprint (KiCad library `{P['fp']}`, renamed; model path `${{KIPRJMOD}}/{STEP}`) |
| `{MPN}.lbr` | Fusion / EAGLE library made from the KiCad symbol and footprint with `adom-lbr generate` (carries MPN, MANUFACTURER, MOUSER_PN, DIGIKEY_PN, LCSC_PN) |
| `{STEP}` | 3D package model, STEP |
| `package-reference.glb` | 3D package model, GLB, clean (no artwork) |
| `{HERO}` | page-viewer hero: the clean model plus footprint artwork (adom-hero 1.4); `{HERO_SC}` lists every pad and label |
{''.join(f"| `{f}` | Altium {'schematic library' if f.endswith('SchLib') else 'PCB library' if f.endswith('PcbLib') else 'adom-lbr hub JSON (symbol, footprint, pin-to-pad map)'}, made with `adom-lbr import-kicad` and `export-altium` |{chr(10)}" for f in ALT)}| `lod/` | board-use GLBs in four levels of detail, with previews and manifest |
## Sources and license
- {P['mfr']}: {ds['title']} ({ds['url']})
{''.join(f'- {x["title"]} ({x["url"]}){chr(10)}' for x in P['extra_docs'])}- KiCad libraries: [{P['fp_lib']}.pretty/{P['fp']}.kicad_mod]({GL}/kicad-footprints/-/blob/master/{P['fp_lib']}.pretty/{P['fp']}.kicad_mod), [{P['fp_lib']}.3dshapes/{P['fp']}.step]({GL}/kicad-packages3D/-/blob/master/{P['fp_lib']}.3dshapes/{P['fp']}.step)
- Murata product data: {P['product_url']} (public API pimapi.murata.com, queried {TODAY})
- Distributor numbers: Mouser, DigiKey and JLCPCB/LCSC through the Adom parts search, {TODAY}
Original Adom content is CC-BY-4.0. The KiCad footprint and 3D files keep their upstream license (CC-BY-SA 4.0 with the
KiCad library exception) and the PDFs remain the publishers' documents; see [LICENSE](LICENSE).
[provenance.json](provenance.json) records every source and file hash; the scripts that built the checks are in `provenance/tools/`.
This is a catalog record for the part, not a manufacturing approval: check the maker's current datasheet for your design.
"""
readme = re.sub(r"\n{3,}", "\n\n", readme)
open(p_("README.md"), "w").write(readme)
# ---------------- page.json / package.json ----------------
title = f"{MPN} - {P['mfr']}"
component = {
"mpn": MPN, "manufacturer": P["mfr"], "package": P["package"], "category": P["category"], "subcategory": P["subcategory"],
"pin_count": 2, "body_size": P["body_mm"],
"value": P["value"], "tolerance": P["tol"], "rating": P["voltage"],
"pins": [{"number": "1", "name": "~"}, {"number": "2", "name": "~"}],
"parts": {"symbol": SYM, "footprint": FP, "lbr": f"{MPN}.lbr", "step": STEP, "model_3d": HERO, "model_3d_plain": "package-reference.glb",
"datasheet": ds["file"], **({"altium_schlib": ALT[0], "altium_pcblib": ALT[1]} if len(ALT) >= 2 else {})},
"manufacturer_links": {"product": P["product_url"], "datasheet_pdf": ds["url"], "datasheet_pim": P["pim_spec_url"]},
"distributor_links": {k: {"part": D[k][0], "url": D[k][1]} for k in ("mouser", "digikey", "lcsc")},
"marking": {"text": None, "statement": P["marking"], "source": P["marking_src"]},
"provenance": {"model_3d": "KiCad library model (no Murata STEP reachable by plain HTTP), checked against Murata's printed L/W/T/e limits", "footprint": "KiCad standard library, renamed",
"hero": "adom-hero 1.4 (adom/component-hero hero_glb.py)",
"land_pattern_check": f"land-pattern/README.md ({land_status})", "omitted": []},
}
tags = ["hardware", "component", "passive", P["category"].lower(), P["package"].split()[0], P["mfr"].lower().replace(" ", "-")]
triggers = [MPN, MPN.lower(), f"{P['value']} {P['package'].split()[0]} {kind_word}"]
eda = {"kicad": {"symbol": SYM, "footprint": FP, "model": STEP, "provenance": "generated"},
"fusion": {"lbr": f"{MPN}.lbr", "provenance": "generated"}}
if len(ALT) >= 2:
eda["altium"] = {"schlib": ALT[0], "pcblib": ALT[1], "provenance": "generated"}
page = {"slug": slug, "title": title, "eda_assets": eda, "type": "hardware", "kind": "component", "version": "0.1.0",
"description": P["brief"], "brief": P["brief"], "tags": tags, "discovery_triggers": triggers, "dependencies": {},
"license": "CC-BY-4.0", "hero": {"path": "docs/hero.png", "chrome": False},
"component": component, "visibility": {"public": True}}
json.dump(page, open(p_("page.json"), "w"), indent=1)
pkg = {"name": slug, "title": title, "version": "0.1.0", "description": P["brief"], "type": "hardware", "kind": "component",
"license": "CC-BY-4.0", "files": []}
json.dump(pkg, open(p_("package.json"), "w"), indent=1)
# ---------------- provenance.json (hashes of everything except itself) ----------------
files = {}
for dp, dn, fn in os.walk(OUT):
for f in fn:
rel = os.path.relpath(os.path.join(dp, f), OUT)
if rel != "provenance.json":
files[rel] = sha(rel)
prov = {
"manufacturer_reference": P["product_url"],
"symbol_source": f"Original, written for this page (standard {kind_word} graphic, non-polar). CC-BY-4.0.",
"footprint_source": KICAD_FP_SRC + f"; renamed {P['fp']} -> {FP[:-10]}, model path set to ${{KIPRJMOD}}/{STEP}; copper unchanged",
"footprint_upstream_sha256": hashlib.sha256(open(os.path.join(ROOT, "dl/kicad", P["fp"] + ".kicad_mod"), "rb").read()).hexdigest(),
"footprint_upstream_url": f"{GL}/kicad-footprints/-/raw/master/{P['fp_lib']}.pretty/{P['fp']}.kicad_mod",
"package_source": KICAD_SRC,
"package_upstream_url": f"{GL}/kicad-packages3D/-/raw/master/{P['fp_lib']}.3dshapes/{P['fp']}.step",
"package_bytes_unchanged": True,
"manufacturer_step": {"found": False, "searched": TODAY, "method": "plain HTTP only (curl), no browser", "note": MSTEP_NOTE},
"datasheet_figures": FIGS,
"hero_glb": {"file": HERO, "sidecar": HERO_SC, "tool": "hero_glb.py (adom/component-hero, adom-hero 1.4), copied into provenance/tools/",
"inputs": ["package-reference.glb", FP, "component.pins (1 and 2, unnamed)"], "result": B["hero_line"].split(": ", 1)[-1]},
"altium": {"files": ALT, "tool": "adom-lbr import-kicad --sym --fp --mpn, then adom-lbr export-altium"},
"geometry_status": "KiCad library package geometry, every printed dimension checked against the datasheet (docs/verify). Not manufacturer-issued CAD.",
"frame": {"units": "mm (STEP), metres (GLB)", "up_axis": "Z", "seat_z": 0, "origin": "footprint origin", "transform": "none"},
"glb": {"tool": "step2glb convert", "exit": 0, "stderr_tail": B["glb_stderr"]},
"hero": {"tool": "step2glb thumbnail --width 1920 --height 1200 (iso, 16:10)", "path": "docs/hero.png"},
"fusion_lbr": {"file": f"{MPN}.lbr", "tool": "adom-lbr generate --sym --fp --part-metadata page.json", "lint": "passed, 0 errors, 0 warnings"},
"datasheet": {"file": ds["file"], "sha256": sha(ds["file"]), "url": ds["url"], "title": ds["title"], "retrieved": TODAY},
"supporting_documents": [{"file": x["file"], "sha256": sha(x["file"]), "url": x["url"], "title": x["title"], "retrieved": TODAY} for x in P["extra_docs"]],
"datasheet_check": {"overlay": f"docs/verify/{slug}-overlay.png", "numbers": f"docs/verify/{slug}-check.json",
"tools": "provenance/tools/ (ortho.py, overlay_chip.py, spec json)", "result": "PASS" if check["all_pass"] else "FAIL"},
"land_pattern_check": {"readme": "land-pattern/README.md", "numbers": "land-pattern/land-check.json", "source": L["source"], "result": land_status},
"marking": {"real_marking": None, "statement": P["marking"], "source": P["marking_src"]},
"distributors": {"tool": "adom-parts-search search " + MPN, "date": TODAY,
**{k: {"part": D[k][0], "url": D[k][1], "stock": D[k][2]} for k in ("mouser", "digikey", "lcsc")}},
"lod": {"manifest": "lod/manifest.json", "generator": "provenance/tools/lod.py", "source_sha256": sha(STEP)},
"tools_note": "lod.py, ortho.py and overlay_chip.py come from the GRM155R71C104KA88D component page; build.py, parts.py, landcheck.py, records.py and push.sh are the scripts used for the GRM1555C1H100JA01D and GRM155R71H103KA88D pages, extended here with datasheet figure crops, the adom-hero 1.4 hero and the Altium lane.",
"files": files,
"files_note": "provenance.json itself is not listed.",
"built_by": "Claude (AI agent) for John Lauer", "date": TODAY,
}
json.dump(prov, open(p_("provenance.json"), "w"), indent=1)
print("records written", slug, land_status)