main
John Lauer Component record for msash168sc7224ktca01: files batch 1 370779c 2d ago
"""Write README.md, land-pattern/README.md, page.json, package.json, models.json, provenance.json,
verification.json and LICENSE for the MSASH168SC7224KTCA01 page.

usage: records.py <slug>   (run after build.py, again after finish.py)
"""
import sys, os, json, hashlib, re, math
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from parts import P, TODAY, ROOT, DOCS, PRODUCT

slug = sys.argv[1]; assert slug == P["slug"]
OUT = f"/home/adom/project/uta-dynos/work/pages/{slug}"
MPN = P["mpn"]; OWNER = "adom"
B = json.load(open(os.path.join(ROOT, "work", f"{slug}-build.json")))
STEP, ORIG, FP, SYM = B["step"], B["orig"], B["fp"], B["sym"]
check = B["check"]
land = json.load(open(os.path.join(OUT, "land-pattern/land-check.json")))
lod = json.load(open(os.path.join(OUT, "lod/manifest.json")))
figs = json.load(open(os.path.join(OUT, "docs/figures/figures.json")))
p_ = lambda f: os.path.join(OUT, f)
ex = lambda f: os.path.exists(p_(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"]; L = P["land"]
DS = {k: dict(local=l, file=f, title=t, url=u) for k, l, f, t, u in DOCS}
ds = DS["catalog"]
by = {"name": "Claude (AI agent) for John Lauer", "handle": "john"}
HERO = f"{slug}-hero.glb"; HERO_SIDE = f"{slug}-hero.overlay.json"; HERO_PNG = "docs/verify/hero-preview.png"
LBR = f"{MPN}.lbr"; SCH = f"altium/{MPN}.SchLib"; PCB = f"altium/{MPN}.PcbLib"; HUB = f"{MPN}.adom-lbr.json"
DXF = f"provenance/manufacturer/{MPN}-land.dxf"
have_hero, have_alt, have_lbr = ex(HERO), ex(SCH) and ex(PCB), ex(LBR)
GL = "https://gitlab.com/kicad/libraries"
F = lambda n: f"docs/figures/{n}"
k = land["kicad"]

# ---------------- land pattern write-up ----------------
t = "| Dimension | KiCad C_0603_1608Metric (used) | Taiyo Yuden land (DXF) | Difference |\n|---|---|---|---|\n"
for r in land["rows"]:
    t += f"| {r['dim']} | {r['kicad_mm']:.2f} mm | {r['maker_nominal']:.2f} mm | {r['difference_mm']:+.2f} mm |\n"
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[:-10]}` so it can sit beside other parts in one library folder. The copper is unchanged. This page compares it with
the land pattern Taiyo Yuden publishes for this exact part number as a DXF drawing on the product page (kept unchanged in
`../{DXF}`): two lands {L['len_b']} mm long and {L['wid_c']} mm wide with a {L['gap_a']} mm gap, {L['overall']} mm overall
(layer L1; the solder resist openings on layer Resist are 0.05 mm larger all round).

![KiCad pads (teal) over Taiyo Yuden's land (amber dashed), true scale](land-pattern.png)

## Numbers

{t}
The DXF carries outlines only, no dimension text; the numbers above are read from its polyline coordinates.

## Result

KiCad's pads ({k['len']:.2f} x {k['wid']:.2f} mm, gap {k['gap']:.2f} mm) are larger than Taiyo Yuden's land on every side and
contain it completely: they are 0.30 mm longer, 0.15 mm wider and reach 0.15 mm further under the part. KiCad generates
this footprint to IPC-7351 nominal density. Both cover the terminal band (e 0.35 +/-0.25 mm) and both are ordinary reflow
lands for a 1608 part; this page keeps the KiCad pads, as its library footprint. A design that wants the maker's land
should make each pad {L['len_b']} x {L['wid_c']} mm with pad centres {L['gap_a'] + L['len_b']:.1f} mm apart. Taiyo Yuden's
precautions document (c_mlcc_prec_e, linked from the product page) warns that excessive solder can cause mechanical stress
that cracks the chip and gives its recommended land sizes for that reason.

The drawing and [land-check.json](land-check.json) come from `provenance/tools/landcheck.py`.
"""
open(p_("land-pattern/README.md"), "w").write(land_readme)

# ---------------- verification.json ----------------
entries = []
ck = [{"text": f"{c['dim']}: model {c['model_mm']:.3f} mm, maker {c['datasheet_min']:.2f} to {c['datasheet_max']:.2f} mm", "result": "pass" if c["pass"] else "fail"} for c in check["checks"]]
ck += [{"text": "the maker's STEP is 0.90 mm thick: the upper limit of T 0.8 +/-0.10 mm and the characteristics sheet's 't = 0.9 mm max'", "result": "note"},
       {"text": "Z-up, mm, seated on z = 0, centred on the footprint origin, long axis on X, as supplied: no transform applied, bytes unchanged", "result": "pass"},
       {"text": "body colour is the maker's (dark grey); the product photo on the characteristics sheet shows a brown body; colours were not changed", "result": "note"}]
entries.append({"date": TODAY, "by": by, "status": "verified", "title": "Taiyo Yuden 3D model checked against the printed 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": "catalog p.5 dimensions", "href": F("ty-cat-p5-dimensions.png")}],
                "files": {STEP: sha(STEP), ORIG: sha(ORIG)}})
lck = [{"text": f"{r['dim']}: KiCad {r['kicad_mm']:.2f} mm, Taiyo Yuden {r['maker_nominal']:.2f} mm ({r['difference_mm']:+.2f} mm)", "result": "note"} for r in land["rows"]]
lck.append({"text": "KiCad's pads contain the maker's land on every side; KiCad footprint kept (renamed, copper unchanged)", "result": "pass"})
entries.append({"date": TODAY, "by": by, "status": "verified", "title": "Footprint compared with Taiyo Yuden's land pattern (KiCad pads larger, kept)",
                "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"}, {"label": "maker DXF", "href": DXF}],
                "files": {FP: sha(FP), DXF: sha(DXF)}})
entries.append({"date": TODAY, "by": by, "status": "verified", "title": "Marking: the real part 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 (a 1.6 x 0.8 mm top has no room for a readable 20-character MPN)", "result": "pass"}],
                "evidence": [], "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, polarity and symbol",
                "scope": ["footprint", "3d-model", "symbol"], "variants": ["*"],
                "checks": [{"text": "non-polar two-terminal capacitor: pads 1 and 2 are interchangeable; symbol pins 1 and 2 map to pads 1 and 2", "result": "pass"},
                           {"text": "symbol pin ends at (0, +2.54) and (0, -2.54) mm, on the 2.54 mm grid", "result": "pass"},
                           {"text": "symbol fields MPN, Manufacturer, Mouser, DigiKey, Adom Wiki, Datasheet and Footprint filled; LCSC left empty because JLCPCB/LCSC list no part (" + TODAY + ")", "result": "note"},
                           {"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)}})
if have_hero:
    entries.append({"date": TODAY, "by": by, "status": "verified", "title": "Hero GLB built to the adom-hero standard",
                    "scope": ["3d-model", "hero"], "variants": ["*"],
                    "checks": [{"text": "built with component-hero hero_glb.py from package-reference.glb, the footprint and page.json; alignment check OK", "result": "pass"},
                               {"text": "teal dashed pad outlines (alpha 0.5), silkscreen (alpha 0.3) and pad-number labels 1 and 2 (alpha 0.5); no pin-1 dot because the part is non-polar", "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", "href": HERO_PNG}, {"label": "overlay record", "href": HERO_SIDE}],
                    "files": {HERO: sha(HERO), "package-reference.glb": sha("package-reference.glb")}})
if have_alt or have_lbr:
    entries.append({"date": TODAY, "by": by, "status": "verified", "title": "Fusion and Altium libraries generated from the KiCad symbol and footprint",
                    "scope": ["symbol", "footprint"], "variants": ["*"],
                    "checks": [{"text": "adom-lbr generate --part-metadata page.json: .lbr lint passed", "result": "pass"},
                               {"text": "adom-lbr import-kicad then assemble-altium --no-dedupe: native .SchLib and .PcbLib written, STEP body embedded", "result": "pass"},
                               {"text": "not yet opened in Fusion or Altium (no desktop app used for this page)", "result": "note"}],
                    "evidence": [], "files": {f: sha(f) for f in (LBR, SCH, PCB, HUB) if ex(f)}})
json.dump({"schema": "adom/verification@1", "part": MPN, "variants": [], "entries": entries}, open(p_("verification.json"), "w"), indent=1)

# ---------------- models.json ----------------
SRC = f"Taiyo Yuden 3D model (STEP) from the product page ({PRODUCT}), bytes unchanged"
models = [
    {"path": STEP, "format": "step", "title": "Package model (STEP, Taiyo Yuden)", "status": "current", "slot": "step", "variant": "plain", "role": "manufacturer",
     "use": ["board-cad"], "marking": None, "unique": "The board CAD model: Taiyo Yuden's own STEP for this part number, bytes unchanged, already Z-up and seated",
     "source": SRC, "sha256": sha(STEP), "git_blob": blob(STEP), "bytes": os.path.getsize(p_(STEP)), "thumb": "docs/models/step-plain.png"},
    {"path": ORIG, "format": "step", "title": "Taiyo Yuden original download (STEP)", "status": "current", "variant": "original", "role": "manufacturer",
     "use": ["archive"], "marking": None, "unique": "The file as downloaded (same bytes as the board model), kept under vendor/ with its original name",
     "source": SRC, "sha256": sha(ORIG), "git_blob": blob(ORIG), "bytes": os.path.getsize(p_(ORIG))},
    {"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": "Clean board-use model for viewers (step2glb), no footprint artwork", "source": SRC,
     "sha256": sha("package-reference.glb"), "git_blob": blob("package-reference.glb"), "bytes": os.path.getsize(p_("package-reference.glb"))},
]
if have_hero:
    models.append({"path": HERO, "format": "glb", "title": "Hero model (GLB)", "status": "current", "slot": "model_3d", "variant": "hero", "role": "derived",
                   "use": ["wiki-hero"], "marking": None, "derived_from": "package-reference.glb",
                   "unique": "The page's 3D view: the package plus teal dashed pad outlines, silkscreen and pad-number labels from the footprint (adom-hero standard)",
                   "source": "component-hero hero_glb.py", "sha256": sha(HERO), "git_blob": blob(HERO), "bytes": os.path.getsize(p_(HERO)), "thumb": HERO_PNG})
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": 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 JSON records and the scripts in provenance/tools/) 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:
- {STEP}, {ORIG}, {DXF} and every model derived from the STEP (package-reference.glb,
  {HERO}, lod/*.glb, the body inside altium/{MPN}.PcbLib) are Taiyo Yuden's 3D and land
  data, provided on Taiyo Yuden's product page, under Taiyo Yuden's terms.
- {FP} is KiCad library content, CC-BY-SA 4.0 with the KiCad library exception
  ({GL}/kicad-footprints); the footprint inside {LBR} and altium/{MPN}.PcbLib keeps that license.
- The PDFs in docs/ and the page crops in docs/figures/ are Taiyo Yuden documents, copyright of
  TAIYO YUDEN CO., LTD., included unchanged (or cropped) for reference.
""")

# ---------------- README ----------------
spec_rows = [
    ("Capacitance", "0.22 uF +/-10 % (code 224, K)", "product page; catalog p.4"),
    ("Rated voltage", "100 V DC (code H)", "product page; catalog p.1"),
    ("Temperature characteristic", "X7S (EIA, code C7): +/-22 % from -55 to +125 C, reference 25 C", "product page; catalog p.3"),
    ("Dissipation factor (tan delta)", "3.5 % max", "product page"),
    ("Insulation resistance", "100 MOhm x uF min (at least 454 MOhm for 0.22 uF)", "product page"),
    ("High temperature loading", "150 % of rated voltage", "product page"),
    ("Operating temperature", "-55 to +125 C", "product page"),
    ("Series", "MSAS medium-high voltage multilayer ceramic capacitor for general purpose; nickel electrodes", "product page; catalog p.1"),
    ("Size", "L 1.6 +/-0.10, W 0.8 +/-0.10, T 0.8 +/-0.10, e 0.35 +/-0.25 (mm); 0.9 mm max", "product page; catalog p.2, p.5; characteristics sheet"),
    ("Packaging (code T)", "178 mm reel, paper tape, 4 mm pitch, 4000 pcs", "product page; catalog p.4, p.6"),
    ("Soldering", "reflow", "product page"),
    ("Compliance", "RoHS, REACH, halogen free", "product page"),
    ("Status", "mass production (preferred)", "product page, " + TODAY),
    ("Previous part number", P["old_pn"], "product page; characteristics sheet"),
]
spec_tbl = "| Parameter | Value | Source |\n|---|---|---|\n" + "".join(f"| {a} | {b} | {c} |\n" for a, b, c in spec_rows)
dist_tbl = ("| Distributor | Part number | Stock seen " + TODAY + " |\n|---|---|---|\n"
            + "".join(f"| {n} | [{D[kk][0]}]({D[kk][1]}) | {D[kk][2]:,} |\n" for kk, n in (("mouser", "Mouser"), ("digikey", "DigiKey")))
            + "| LCSC / JLCPCB | not listed | - |\n")
fig_tbl = "| Figure | Source |\n|---|---|\n" + "".join(f"| [{f['caption']}]({f['file']}) | {f['document']}, p.{f['page']} |\n" for f in figs)
cad_rows = [(f"`{SYM}`", "KiCad symbol (standard capacitor graphic, pin ends on the 2.54 mm grid) with MPN, Manufacturer, Mouser, DigiKey, LCSC (empty) and Adom Wiki fields"),
            (f"`{FP}`", f"KiCad footprint (KiCad library `{P['fp']}`, renamed; model path `${{KIPRJMOD}}/{STEP}`)")]
if have_lbr: cad_rows.append((f"`{LBR}`", "Fusion / EAGLE library made with `adom-lbr generate`"))
if have_alt: cad_rows.append((f"`{SCH}`, `{PCB}`", f"Altium symbol and footprint libraries (native binary) made with `adom-lbr import-kicad` and `assemble-altium` (the PcbLib embeds the STEP body); `{HUB}` is the adom-lbr hub record"))
cad_rows += [(f"`{STEP}`", "3D model, STEP: Taiyo Yuden's own, bytes unchanged"), (f"`{ORIG}`", "the same file under its download name"),
             ("`package-reference.glb`", "3D model, GLB (clean, for board viewers)")]
if have_hero: cad_rows.append((f"`{HERO}`", "the page's 3D view: package plus footprint pad outlines, silkscreen and pad labels"))
cad_rows += [("`lod/`", "board-use GLBs in four levels of detail, with previews and manifest"), (f"`{DXF}`", "Taiyo Yuden's land pattern drawing for this part, unchanged")]
cad_tbl = "| File | What it is |\n|---|---|\n" + "".join(f"| {a} | {b} |\n" for a, b in cad_rows)
doc_rows = [("Product page", PRODUCT)] + [(DS[kk]["title"], f"[PDF]({DS[kk]['file']}) ([source]({DS[kk]['url']}))") for kk in ("catalog", "chars", "pack")]
doc_rows += [("3D model and land pattern", "STEP and DXF downloads on the product page (copies on this page)"),
             ("Reliability data, precautions, SPICE and S-parameter models", "links on the product page")]
res_tbl = "| Resource | Link |\n|---|---|\n" + "".join(f"| {a} | {b} |\n" for a, b in doc_rows)
hero_para = (f"""
The page viewer shows `{HERO}`: the clean model plus nonphysical reference artwork drawn under it to the adom-hero
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 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.

![Hero preview, top and oblique]({HERO_PNG})
""" if have_hero else "")
readme = f"""# {MPN}

Taiyo Yuden {MPN}: {P['brief']}. A medium-high voltage multilayer ceramic capacitor from Taiyo Yuden's MSAS general purpose
series, previously sold as {P['old_pn']}. Taiyo Yuden names inverters and DC/DC converters as its main applications.

::adom-verified{{src="/{OWNER}/{slug}/render/verification.json"}}

| Field | Value |
|---|---|
| Manufacturer | {P['mfr_full']} |
| Manufacturer part number | {MPN} (previously {P['old_pn']}) |
| Type | {P['subcategory']} |
| Capacitance | {P['value']}, {P['tol']} |
| Rated voltage | 100 V DC |
| Dielectric | X7S, +/-22 % over -55 to +125 C |
| Package | {P['package']}, 1.6 x 0.8 x 0.8 mm (0.9 mm max) |
| Marking | none |
| Datasheet | [{ds['title']}]({ds['file']}) and the [characteristics data]({DS['chars']['file']}) |

## Key specifications

{spec_tbl}
The product page was read on {TODAY}; catalog page numbers refer to [the catalog PDF]({ds['file']}).

## DC bias: plan for the capacitance you get at your voltage

X7S is a class 2 dielectric, so the capacitance falls as DC voltage is applied. Taiyo Yuden's characteristics sheet plots it:
read off that graph, the capacitance is roughly 30 % lower at 20 V, 60 % lower at 40 V, 80 % lower at 60 V and close to
90 % lower at the 100 V rating (values read from the plotted curve, not printed numbers). The same sheet gives capacitance
and impedance against frequency, the temperature curve and ripple current heating.

![Electrical characteristics data](docs/figures/ty-chars-p1.png)

## Ordering

{dist_tbl}
Stock figures are a snapshot from the Adom parts search on {TODAY}; check the distributor before ordering. DigiKey listed
the part with no stock that day; Mouser had {D['mouser'][2]:,}. {P['lcsc_note']}. Mouser also lists the previous part
number {P['old_pn']} and points it to this one.

## Part number

M S A S (two-terminal general purpose MLCC) | H 100 V | 16 size 1608 | 8 thickness 0.8 mm | S dimension tolerance |
C7 X7S | 224 0.22 uF | K +/-10 % | T 178 mm reel, 4 mm pitch tape | CA01 internal code (catalog p.1 to p.4).

![Catalog p.1 part numbering](docs/figures/ty-cat-p1-part-number.png)

## Manufacturer and resources

{res_tbl}
## Pins

| Pin | Function |
|---|---|
| 1 | terminal (non-polar) |
| 2 | terminal (non-polar) |

A ceramic capacitor has no polarity: either terminal may face either way.

## Land pattern: KiCad footprint vs Taiyo Yuden

<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>&nbsp;&nbsp;&nbsp;&nbsp;Land&nbsp;pattern&nbsp;&nbsp;&nbsp;&nbsp;</sub></td>
<td>The footprint is KiCad's standard `{P['fp']}`, and this guide lays its pads over the land Taiyo Yuden publishes for this part as a DXF, at true scale. KiCad's pads are larger on every side and contain the maker's land. Use it to decide whether to keep the KiCad pads or change them to Taiyo Yuden's {L['len_b']} x {L['wid_c']} mm lands before layout.<br><br><a href="land-pattern/README.md">The comparison, with the numbers</a></td>
</tr>
</table>

## 3D model

The model is **Taiyo Yuden's own STEP** for this part number, downloaded by plain HTTP from the product page and used with
its bytes unchanged: it is already in millimetres, Z-up, seated on z = 0 and centred, so no transform was needed. It is
1.60 x 0.80 x 0.90 mm with 0.35 mm terminal bands; the 0.90 mm height is the top of the printed range (T 0.8 +/-0.10 mm, and
the characteristics sheet says 0.9 mm max). The body colour is the maker's dark grey (their product photo shows a brown body);
it was left as supplied. The part is unmarked, so no marking is shown.
{hero_para}
::adom-models{{src="/{OWNER}/{slug}/render/models.json"}}

## Model checked against the printed dimensions

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 limits (nominal outline, tolerance limits, allowed terminal band). Every dimension is inside the printed
range: {", ".join(f"{c['dim']} {c['model_mm']:.3f} mm ({c['datasheet_min']:.2f} to {c['datasheet_max']:.2f})" for c in check['checks'])}.

![Model over the printed dimensions](docs/verify/{slug}-overlay.png)

Source: {P['dims_text']}.

## Board levels of detail

Clean board-use GLBs (no footprint pads, outlines, labels or silkscreen) 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

{cad_tbl}
## Document figures

The pages this record cites, cropped from the PDFs:

{fig_tbl}
## Sources and license

- Taiyo Yuden product page: {PRODUCT} (specifications, documents, STEP and DXF downloads), {TODAY}
- KiCad library footprint: [{P['fp_lib']}.pretty/{P['fp']}.kicad_mod]({GL}/kicad-footprints/-/blob/master/{P['fp_lib']}.pretty/{P['fp']}.kicad_mod)
- Distributor numbers: Mouser, DigiKey and JLCPCB/LCSC through the Adom parts search, {TODAY}

Original Adom content is CC-BY-4.0. Taiyo Yuden's STEP, DXF and PDFs keep Taiyo Yuden's terms and the KiCad footprint keeps
its upstream license (CC-BY-SA 4.0 with the KiCad library exception); 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 Taiyo Yuden's current specification for your design.
"""
readme = re.sub(r"\n{3,}", "\n\n", readme)
for bad in ("—", "–"):
    assert bad not in readme and bad not in land_readme, "dash in README"
open(p_("README.md"), "w").write(readme)

# ---------------- page.json / package.json ----------------
title = f"{MPN} - {P['mfr']}"
parts_ = {"symbol": SYM, "footprint": FP, "step": STEP, "model_3d": HERO if have_hero else "package-reference.glb",
          "model_3d_plain": "package-reference.glb", "datasheet": ds["file"]}
if have_lbr: parts_["lbr"] = LBR
if have_alt: parts_.update({"altium_schlib": SCH, "altium_pcblib": PCB})
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": "100 V DC", "dielectric": "X7S (+/-22 %, -55 to +125 C)",
    "previous_part_number": P["old_pn"],
    "pins": [{"number": "1", "name": "~"}, {"number": "2", "name": "~"}],
    "parts": parts_,
    "manufacturer_links": {"product": PRODUCT, "datasheet_pdf": ds["url"], "characteristics_pdf": DS["chars"]["url"], "packaging_pdf": DS["pack"]["url"],
                           "step": "https://ds.yuden.co.jp/TYCOMPAS/ut/download?pn=MSASH168SC7224KTCA01&fileType=3D_Filename",
                           "land_dxf": "https://ds.yuden.co.jp/TYCOMPAS/ut/download?pn=MSASH168SC7224KTCA01&fileType=land_Filename"},
    "distributor_links": {"mouser": {"part": D["mouser"][0], "url": D["mouser"][1]}, "digikey": {"part": D["digikey"][0], "url": D["digikey"][1]}, "lcsc": False},
    "lcsc_note": P["lcsc_note"],
    "marking": {"text": None, "statement": P["marking"], "source": P["marking_src"]},
    "provenance": {"model_3d": "Taiyo Yuden STEP for this part number, bytes unchanged, checked against the printed dimensions",
                   "footprint": "KiCad standard library, renamed", "land_pattern_check": "land-pattern/README.md (KiCad pads larger than the maker's DXF land)", "omitted": []},
}
eda = {"kicad": {"symbol": SYM, "footprint": FP, "model": STEP, "provenance": "generated"}}
if have_lbr: eda["fusion"] = {"lbr": LBR, "provenance": "generated"}
if have_alt: eda["altium"] = {"schlib": SCH, "pcblib": PCB, "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": ["hardware", "component", "passive", "capacitor", "mlcc", "0603", "x7s", "100v", "taiyo-yuden"],
        "discovery_triggers": [MPN, MPN.lower(), P["old_pn"], "0.22 uF 100 V 0603 X7S", "220nF 100V 0603 capacitor"],
        "dependencies": {}, "license": "CC-BY-4.0", "hero": {"path": "docs/hero.png", "chrome": False},
        "component": component, "visibility": {"public": True}, "source": {"tool": "taiyo-yuden", "url": PRODUCT}}
json.dump(page, open(p_("page.json"), "w"), indent=1)
json.dump({"name": slug, "title": title, "version": "0.1.0", "description": P["brief"], "type": "hardware", "kind": "component",
           "license": "CC-BY-4.0", "files": []}, open(p_("package.json"), "w"), indent=1)

# ---------------- provenance.json ----------------
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)
finp = os.path.join(ROOT, "work", f"{slug}-finish.json")
fin = json.load(open(finp)) if os.path.exists(finp) else {}
prov = {
    "manufacturer_reference": PRODUCT,
    "product_page_specs_read": {"date": TODAY, "status": "Mass Production (Preferred)", "capacitance": "0.22 uF +/-10 %", "rated_voltage": "100 V",
                                "tan_delta_max": "3.5 %", "temperature_characteristic": "X7S", "operating_temperature": "-55 to +125 C",
                                "capacitance_change": "+/-22 %", "high_temperature_loading": "150 % rated voltage", "insulation_resistance_min": "100 MOhm uF",
                                "dimensions_mm": {"L": "1.6 +/-0.10", "W": "0.8 +/-0.10", "T": "0.8 +/-0.10", "e": "0.35 +/-0.25"},
                                "standard_quantity": "paper tape 4000 pcs", "soldering": "reflow", "rohs": True, "reach": True, "halogen_free": True,
                                "previous_part_number": P["old_pn"]},
    "symbol_source": "Original, written for this page (standard capacitor graphic, non-polar, pin ends on the 2.54 mm grid). CC-BY-4.0.",
    "footprint_source": f"KiCad standard library {P['fp_lib']}:{P['fp']} (CC-BY-SA 4.0 with the KiCad library exception); renamed {FP[:-10]}, model path ${{KIPRJMOD}}/{STEP}; copper unchanged",
    "footprint_upstream_url": f"{GL}/kicad-footprints/-/raw/master/{P['fp_lib']}.pretty/{P['fp']}.kicad_mod",
    "footprint_upstream_sha256": hashlib.sha256(open(os.path.join(ROOT, "dl/kicad", P["fp"] + ".kicad_mod"), "rb").read()).hexdigest(),
    "manufacturer_step": {"found": True, "url": "https://ds.yuden.co.jp/TYCOMPAS/ut/download?pn=MSASH168SC7224KTCA01&fileType=3D_Filename",
                          "method": "plain HTTP GET with the session cookie and referer of the product page (no browser)", "retrieved": TODAY,
                          "file": ORIG, "sha256": sha(ORIG), "units": "mm", "up_axis": "Z", "seat_z": 0, "transform": "none",
                          "colours": "as supplied (body 0.25 grey, terminals 0.75 grey, sRGB)"},
    "manufacturer_land": {"file": DXF, "sha256": sha(DXF), "url": "https://ds.yuden.co.jp/TYCOMPAS/ut/download?pn=MSASH168SC7224KTCA01&fileType=land_Filename",
                          "read": "layer L1 lands x 0.4 to 1.0 mm, y -0.4 to 0.4 mm (both sides); layer Resist 0.05 mm larger; layer Comp body 1.6 x 0.8 mm"},
    "geometry_status": "Manufacturer-issued CAD for this part number, every printed dimension checked (docs/verify).",
    "marking": {"real_marking": None, "statement": P["marking"], "source": P["marking_src"]},
    "frame": {"units": "mm (STEP), metres (GLB)", "up_axis": "Z", "seat_z": 0, "origin": "footprint origin", "transform": "none"},
    "glb": {"tool": "step2glb convert (service)", "source": STEP, "exit": 0},
    "hero_image": {"tool": "step2glb thumbnail --width 1600 --height 1000 (iso, 16:10)", "source": STEP, "path": "docs/hero.png"},
    "hero_glb": fin.get("hero"), "fusion_lbr": fin.get("lbr"), "altium": fin.get("altium"),
    "documents": [{"file": DS[kk]["file"], "sha256": sha(DS[kk]["file"]), "url": DS[kk]["url"], "title": DS[kk]["title"], "retrieved": TODAY} for kk in ("catalog", "chars", "pack")],
    "document_figures": figs,
    "dc_bias_reading": "values in the README read off the plotted DC bias curve of 22U2A07 (approximate): about -30 % at 20 V, -60 % at 40 V, -80 % at 60 V, -90 % near 100 V",
    "datasheet_check": {"overlay": f"docs/verify/{slug}-overlay.png", "numbers": f"docs/verify/{slug}-check.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": "KiCad pads larger than and containing the maker's land; KiCad kept"},
    "distributors": {"tool": "adom-parts-search search " + MPN, "date": TODAY,
                     "mouser": {"part": D["mouser"][0], "url": D["mouser"][1], "stock": D["mouser"][2]},
                     "digikey": {"part": D["digikey"][0], "url": D["digikey"][1], "stock": D["digikey"][2]}, "lcsc": None, "lcsc_note": P["lcsc_note"]},
    "lod": {"manifest": "lod/manifest.json", "generator": "provenance/tools/lod.py", "source": STEP, "source_sha256": sha(STEP)},
    "tools_note": "Scripts adapted from adom/rc0402fr-0733kl and adom/grm188r72a103ka01d.",
    "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, "hero" if have_hero else "-", "altium" if have_alt else "-", "lbr" if have_lbr else "-")