component
GJM1555C1H1R0WB01D - Murata
Public Unreviewedby John Lauer
MLCC 1.0 pF +/-0.05 pF, 50 V, C0G (high Q, GJM series), 0402 (1005 metric)
← Commit history
Remove the AI-added MPN-etched model (the part has no marking per the Murata reference sheet; John's rule: real marking only). Add datasheet, model-vs-datasheet overlay proof, verification log, clean board-use LOD GLBs, honest models.json
21 files changed
+1031−125
README.md+26−6docs/gjm1555c1h1r0wb01d-datasheet.pdfdocs/verify/gjm1555c1h1r0wb01d-check.json+51docs/verify/gjm1555c1h1r0wb01d-overlay.pnglod/high.glblod/high.pnglod/low.glblod/low.pnglod/manifest.json+178lod/medium.glblod/medium.pnglod/super-low.glblod/super-low.pngmodels.json+89−55package.json+8−4provenance.json+54−60provenance/tools/gjm1555c1h1r0wb01d-overlay-spec.json+39provenance/tools/lod.py+170provenance/tools/ortho.py+123provenance/tools/overlay_chip.py+146verification.json+147README.md+26−6@@ -27,16 +27,36 @@ The symbol is an original KiCad symbol made from the manufacturer pin list. The This is a development catalog record, not manufacturing approval. -## 3D models+## 3D model++This page has one 3D model and it carries no marking: `package-reference.step` (board CAD) and+`package-reference.glb` (page viewer), plus four clean board-use levels of detail in `lod/`.+The Murata reference sheet specifies no marking for this part: none of its 29 pages has a marking section or mentions a part marking, and the p.2 dimension sketch shows a plain body (GJM1555C1H1R0WB01-01A, all 29 pages searched; p.2), so the model shows a plain body, as the real part looks.+Adom's rule is that a 3D chip shows only the manufacturer's real part marking; an AI-added+part-number etch that was published here on 2026-10-19 was removed for that reason (see provenance.json). ::adom-models{src="/john/gjm1555c1h1r0wb01d/render/models.json"} -| File | What it is |-|---|---|-| package-reference.step / package-reference.glb | Plain KiCad package model (C_0402_1005Metric generic geometry), no marking. |-| gjm1555c1h1r0wb01d-mpn.step / gjm1555c1h1r0wb01d-mpn.glb | The same model with the part number GJM1555C1H1R0WB01D laser-etched along the body, upright, between the end caps (virtual identification mark added by Adom tooling, not the manufacturer's marking). Use this STEP for board CAD when you want the part named in 3D. |+## Model checked against the datasheet++The datasheet gives a dimension table with an unscaled sketch, so the model was rendered in+orthographic top, bottom, side and end views at a true 300 px/mm 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: L 1.000 mm (datasheet 0.95 to 1.05); W 0.500 mm (datasheet 0.45 to 0.55); T/H 0.500 mm (datasheet 0.45 to 0.55); e (terminal band), top 0.200 mm (datasheet 0.15 to 0.35); e (terminal band), bottom 0.200 mm (datasheet 0.15 to 0.35). The model is Z-up, seated on z = 0, centred on the+footprint origin, long axis on X.++++Source: Murata reference sheet GJM1555C1H1R0WB01-01A, p.2 'Type & Dimension', size code 1005M(0402): L 1.0 +/-0.05, W 0.5 +/-0.05, T 0.5 +/-0.05, e 0.15 to 0.35, g 0.3 min (mm). [Datasheet PDF](docs/gjm1555c1h1r0wb01d-datasheet.pdf)++::adom-verified{src="/john/gjm1555c1h1r0wb01d/render/verification.json"}++## 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. -The marked model was made with adom-aiflow-etch from package-reference.step and component.kicad_mod and passed its legibility gate (docs/gjm1555c1h1r0wb01d-mpn.check.png). provenance.json records the source hash, tool versions, parameters and the gate result.+::adom-component-lod{src="/john/gjm1555c1h1r0wb01d/render/lod/manifest.json"} ## Used in
docs/gjm1555c1h1r0wb01d-datasheet.pdfadded⋯ 1 unchanged line ⋯
docs/verify/gjm1555c1h1r0wb01d-check.jsonadded+51@@ -0,0 +1,51 @@+{+ "step": "package-reference.step",+ "bbox_mm": [+ -0.5,+ 0.5,+ -0.25,+ 0.25,+ 0.0,+ 0.5+ ],+ "checks": [+ {+ "dim": "L",+ "model_mm": 1.0,+ "datasheet_min": 0.95,+ "datasheet_max": 1.05,+ "pass": true+ },+ {+ "dim": "W",+ "model_mm": 0.5,+ "datasheet_min": 0.45,+ "datasheet_max": 0.55,+ "pass": true+ },+ {+ "dim": "T/H",+ "model_mm": 0.5,+ "datasheet_min": 0.45,+ "datasheet_max": 0.55,+ "pass": true+ },+ {+ "dim": "e (terminal band), top",+ "model_mm": 0.2,+ "datasheet_min": 0.15,+ "datasheet_max": 0.35,+ "pass": true+ },+ {+ "dim": "e (terminal band), bottom",+ "model_mm": 0.2,+ "datasheet_min": 0.15,+ "datasheet_max": 0.35,+ "pass": true+ }+ ],+ "z_up_seated_centred": true,+ "long_axis_x": true,+ "all_pass": true+}
docs/verify/gjm1555c1h1r0wb01d-overlay.pngadded⋯ 1 unchanged line ⋯
lod/high.glbadded⋯ 1 unchanged line ⋯
lod/high.pngadded⋯ 1 unchanged line ⋯
lod/low.glbadded⋯ 1 unchanged line ⋯
lod/low.pngadded⋯ 1 unchanged line ⋯
lod/manifest.jsonadded+178@@ -0,0 +1,178 @@+{+ "schema": "adom/component-lod@1",+ "mpn": "GJM1555C1H1R0WB01D",+ "upAxis": "Z",+ "units": "metres",+ "status": "candidate (AI-checked, not human-reviewed)",+ "purpose": "clean board-use component models: no footprint pads, outlines, labels or silkscreen; no marking (none per datasheet)",+ "source": {+ "path": "package-reference.step",+ "sha256": "03e44c4b2b727b8d4b6b1fd598cbd12dd5e614f1dfeefa96a92a07e3a3b312a2"+ },+ "sourceToDisplay": "uniform scale 0.001 (mm to m); no rotation, no translation; origin = footprint origin, seat z = 0",+ "generator": "lod.py (capencoder passives, OCCT via cadquery-ocp)",+ "removedHelpers": [],+ "levels": [+ {+ "level": "high",+ "path": "lod/high.glb",+ "sha256": "68acbdcb91d8e05c66efc501487869191d7f8d9109a28a9783d28651d4b45dac",+ "sizeBytes": 68412,+ "decodedBufferBytes": 66504,+ "triangles": 1580,+ "primitives": 2,+ "bounds": {+ "min": [+ -0.0005,+ -0.00025,+ 0.0+ ],+ "max": [+ 0.0005,+ 0.00025,+ 0.0005+ ]+ },+ "approximation": {+ "method": "OCCT BRepMesh_IncrementalMesh",+ "linearDeflectionMm": 0.0005,+ "angularDeflectionRad": 0.1+ },+ "detailVisibility": {+ "virtualMpn": false,+ "note": "no marking: the datasheet specifies none"+ },+ "review": "Candidate: rendered preview checked by AI (silhouette, terminals, seat z=0); not human-reviewed",+ "preview": {+ "path": "lod/high.png",+ "sha256": "5a82b4e7293806d37477e6ded56b7c038ead8ba9bcd20e913707190ed7e52a81",+ "sizeBytes": 8646,+ "width": 400,+ "height": 300,+ "view": "iso",+ "sourceLevelSha256": "68acbdcb91d8e05c66efc501487869191d7f8d9109a28a9783d28651d4b45dac"+ }+ },+ {+ "level": "medium",+ "path": "lod/medium.glb",+ "sha256": "4472dbe7df7a96447c93f320a683313eeee31981de733eaa9dd185ea0d0422a2",+ "sizeBytes": 22040,+ "decodedBufferBytes": 20136,+ "triangles": 476,+ "primitives": 2,+ "bounds": {+ "min": [+ -0.0005,+ -0.00025,+ 0.0+ ],+ "max": [+ 0.0005,+ 0.00025,+ 0.0005+ ]+ },+ "approximation": {+ "method": "OCCT BRepMesh_IncrementalMesh",+ "linearDeflectionMm": 0.004,+ "angularDeflectionRad": 0.35+ },+ "detailVisibility": {+ "virtualMpn": false,+ "note": "no marking: the datasheet specifies none"+ },+ "review": "Candidate: rendered preview checked by AI (silhouette, terminals, seat z=0); not human-reviewed",+ "preview": {+ "path": "lod/medium.png",+ "sha256": "94fb3d11951575a3e400b4c374cc637fb360a053e5f31ac8e9256923bc9417ef",+ "sizeBytes": 8520,+ "width": 400,+ "height": 300,+ "view": "iso",+ "sourceLevelSha256": "4472dbe7df7a96447c93f320a683313eeee31981de733eaa9dd185ea0d0422a2"+ }+ },+ {+ "level": "low",+ "path": "lod/low.glb",+ "sha256": "97c28a2b6729215f6ac551885121829bfecd90f647b98917dda123fd5dca1c29",+ "sizeBytes": 11952,+ "decodedBufferBytes": 10056,+ "triangles": 236,+ "primitives": 2,+ "bounds": {+ "min": [+ -0.0005,+ -0.00025,+ 0.0+ ],+ "max": [+ 0.0005,+ 0.00025,+ 0.0005+ ]+ },+ "approximation": {+ "method": "OCCT BRepMesh_IncrementalMesh",+ "linearDeflectionMm": 0.02,+ "angularDeflectionRad": 0.9+ },+ "detailVisibility": {+ "virtualMpn": false,+ "note": "no marking: the datasheet specifies none"+ },+ "review": "Candidate: rendered preview checked by AI (silhouette, terminals, seat z=0); not human-reviewed",+ "preview": {+ "path": "lod/low.png",+ "sha256": "f6faafada8e41927fd1d00c97d5037be6faf3aec88bb750339014e5ef6c87492",+ "sizeBytes": 8597,+ "width": 400,+ "height": 300,+ "view": "iso",+ "sourceLevelSha256": "97c28a2b6729215f6ac551885121829bfecd90f647b98917dda123fd5dca1c29"+ }+ },+ {+ "level": "super-low",+ "path": "lod/super-low.glb",+ "sha256": "f43ec4fa773604481f92061a583e06c0a643e4fd35ec0d9b2ebe1979f7ba50f9",+ "sizeBytes": 3828,+ "decodedBufferBytes": 1944,+ "triangles": 36,+ "primitives": 2,+ "bounds": {+ "min": [+ -0.0005,+ -0.00025,+ 0.0+ ],+ "max": [+ 0.0005,+ 0.00025,+ 0.0005+ ]+ },+ "approximation": {+ "method": "per-material bounding boxes split at the part centre (terminals, body, overcoat)"+ },+ "detailVisibility": {+ "virtualMpn": false,+ "note": "no marking: the datasheet specifies none"+ },+ "review": "Candidate: rendered preview checked by AI (silhouette, terminals, seat z=0); not human-reviewed",+ "preview": {+ "path": "lod/super-low.png",+ "sha256": "e9c1f448878cdfb09bb08dd62222cb224dab0177841ff7f459af55a1eca73eb3",+ "sizeBytes": 7245,+ "width": 400,+ "height": 300,+ "view": "iso",+ "sourceLevelSha256": "f43ec4fa773604481f92061a583e06c0a643e4fd35ec0d9b2ebe1979f7ba50f9"+ }+ }+ ],+ "hints": [+ "All four levels share pose and units; super-low is boxes per material group, chosen for board overviews."+ ]+}
lod/medium.glbadded⋯ 1 unchanged line ⋯
lod/medium.pngadded⋯ 1 unchanged line ⋯
lod/super-low.glbadded⋯ 1 unchanged line ⋯
lod/super-low.pngadded⋯ 1 unchanged line ⋯
models.json+89−55@@ -3,88 +3,122 @@ "part": "GJM1555C1H1R0WB01D", "models": [ {- "path": "gjm1555c1h1r0wb01d-mpn.glb",- "format": "glb",- "title": "MPN-marked model (GLB)",- "status": "current",- "slot": "model_3d_marked",- "variant": "etched",- "use": [- "render"- ],- "unique": "Part number GJM1555C1H1R0WB01D etched on top, for viewers",- "marking": {- "text": "GJM1555C1H1R0WB01D",- "surface": "top, along the long axis between the end caps",- "colour": "light"- },- "sha256": "77700f5a864ca256bf64653134649c826cc38eb26e8ae79e8d644680b5f05550",- "git_blob": "edd90be01919e44b49ea31378796f563623c559d",- "bytes": 30064,- "derived_from": "gjm1555c1h1r0wb01d-mpn.step",- "role": "derived",- "source": "KiCad packages3D (CC-BY-SA 4.0 with exception)"- },- {- "path": "gjm1555c1h1r0wb01d-mpn.step",+ "path": "package-reference.step", "format": "step",- "title": "MPN-marked model (STEP)",+ "title": "Package model (STEP)", "status": "current",- "slot": "model_3d_step",- "variant": "etched",+ "slot": "step",+ "variant": "plain",+ "role": "derived", "use": [ "board-cad" ],- "unique": "Part number GJM1555C1H1R0WB01D etched on top",- "marking": {- "text": "GJM1555C1H1R0WB01D",- "surface": "top, along the long axis between the end caps",- "colour": "light"- },- "sha256": "6329e4a8a041a01ce2ea55cfffee867ed47519bea76ea04a857211b188d3ffce",- "git_blob": "49394bbc2332471d465d3d51536e074da97fb45f",- "bytes": 384954,- "derived_from": "package-reference.step",- "role": "derived",- "source": "KiCad packages3D (CC-BY-SA 4.0 with exception)"+ "marking": null,+ "unique": "The board CAD model: KiCad library package geometry, checked against the datasheet table, no marking (the part has none)",+ "source": "KiCad packages3D Capacitor_SMD.3dshapes/C_0402_1005Metric.step (CC-BY-SA 4.0 with exception)",+ "sha256": "03e44c4b2b727b8d4b6b1fd598cbd12dd5e614f1dfeefa96a92a07e3a3b312a2",+ "git_blob": "a5725e143e2db0a0a1e499bd95c9c9da94a4f82b",+ "bytes": 43502 }, { "path": "package-reference.glb", "format": "glb",- "title": "Plain package model (GLB)",+ "title": "Package model (GLB)", "status": "current", "slot": "model_3d", "variant": "plain",+ "role": "derived", "use": [ "render" ],- "unique": "Unmarked, for the page viewer", "marking": null,+ "derived_from": "package-reference.step",+ "unique": "The same model for the page viewer (step2glb)",+ "source": "KiCad packages3D Capacitor_SMD.3dshapes/C_0402_1005Metric.step (CC-BY-SA 4.0 with exception)", "sha256": "100935c5c0d6f20a6c9bdfb6386f874dc8a4457bf0e4f3ace69f1ab090100773", "git_blob": "be1969cc5b4bde9fe0ba1de99674dbb12e76fd76",- "bytes": 15944,+ "bytes": 15944+ },+ {+ "path": "lod/high.glb",+ "format": "glb",+ "title": "Board LOD, high",+ "status": "current",+ "variant": "lod",+ "role": "derived",+ "use": [+ "board-insertion",+ "render"+ ],+ "marking": null, "derived_from": "package-reference.step",+ "unique": "high detail for board and scaffold viewers: 1580 triangles, 2 primitives",+ "thumb": "lod/high.png",+ "source": "KiCad packages3D Capacitor_SMD.3dshapes/C_0402_1005Metric.step (CC-BY-SA 4.0 with exception)",+ "sha256": "68acbdcb91d8e05c66efc501487869191d7f8d9109a28a9783d28651d4b45dac",+ "git_blob": "4ebce4d7f464b6de150bed23810ebfefbd331095",+ "bytes": 68412+ },+ {+ "path": "lod/medium.glb",+ "format": "glb",+ "title": "Board LOD, medium",+ "status": "current",+ "variant": "lod", "role": "derived",- "source": "KiCad packages3D (CC-BY-SA 4.0 with exception)"+ "use": [+ "board-insertion",+ "render"+ ],+ "marking": null,+ "derived_from": "package-reference.step",+ "unique": "medium detail for board and scaffold viewers: 476 triangles, 2 primitives",+ "thumb": "lod/medium.png",+ "source": "KiCad packages3D Capacitor_SMD.3dshapes/C_0402_1005Metric.step (CC-BY-SA 4.0 with exception)",+ "sha256": "4472dbe7df7a96447c93f320a683313eeee31981de733eaa9dd185ea0d0422a2",+ "git_blob": "457bce4372c0d36a448547f4da29dba2c1603486",+ "bytes": 22040 }, {- "path": "package-reference.step",- "format": "step",- "title": "Plain package model (STEP)",+ "path": "lod/low.glb",+ "format": "glb",+ "title": "Board LOD, low", "status": "current",- "slot": "step",- "variant": "plain",+ "variant": "lod",+ "role": "derived", "use": [- "board-cad"+ "board-insertion",+ "render" ],- "unique": "Unmarked KiCad package geometry", "marking": null,- "sha256": "03e44c4b2b727b8d4b6b1fd598cbd12dd5e614f1dfeefa96a92a07e3a3b312a2",- "git_blob": "a5725e143e2db0a0a1e499bd95c9c9da94a4f82b",- "bytes": 43502,+ "derived_from": "package-reference.step",+ "unique": "low detail for board and scaffold viewers: 236 triangles, 2 primitives",+ "thumb": "lod/low.png",+ "source": "KiCad packages3D Capacitor_SMD.3dshapes/C_0402_1005Metric.step (CC-BY-SA 4.0 with exception)",+ "sha256": "97c28a2b6729215f6ac551885121829bfecd90f647b98917dda123fd5dca1c29",+ "git_blob": "f4f23d540497844e3085db8ce231cf84c57e93bf",+ "bytes": 11952+ },+ {+ "path": "lod/super-low.glb",+ "format": "glb",+ "title": "Board LOD, super-low",+ "status": "current",+ "variant": "lod", "role": "derived",- "source": "KiCad packages3D (CC-BY-SA 4.0 with exception)"+ "use": [+ "board-insertion",+ "render"+ ],+ "marking": null,+ "derived_from": "package-reference.step",+ "unique": "super-low detail for board and scaffold viewers: 36 triangles, 2 primitives",+ "thumb": "lod/super-low.png",+ "source": "KiCad packages3D Capacitor_SMD.3dshapes/C_0402_1005Metric.step (CC-BY-SA 4.0 with exception)",+ "sha256": "f43ec4fa773604481f92061a583e06c0a643e4fd35ec0d9b2ebe1979f7ba50f9",+ "git_blob": "c40117d77e9f7b7d4e3fca3f15898a02c55abcab",+ "bytes": 3828 } ], "updated": "2026-10-19"-}+}
package.json+8−4@@ -21,7 +21,8 @@ "manufacturer": "Murata", "package": "C_0402_1005Metric", "manufacturer_links": {- "datasheet": "https://www.murata.com/en-us/products/productdetail?partno=GJM1555C1H1R0WB01%23"+ "datasheet": "https://www.murata.com/en-us/products/productdetail?partno=GJM1555C1H1R0WB01%23",+ "datasheet_pdf": "https://search.murata.co.jp/Ceramy/image/img/A01X/G101/ENG/GJM1555C1H1R0WB01-01A.pdf" }, "pins": [ {@@ -39,9 +40,7 @@ "footprint": "component.kicad_mod", "step": "package-reference.step", "model_3d": "package-reference.glb",- "model_3d_step": "gjm1555c1h1r0wb01d-mpn.step",- "model_3d_step_plain": "package-reference.step",- "model_3d_marked": "gjm1555c1h1r0wb01d-mpn.glb"+ "datasheet": "docs/gjm1555c1h1r0wb01d-datasheet.pdf" }, "provenance": { "omitted": []@@ -49,6 +48,11 @@ "distributor_links": { "mouser": "81-GJM1555C1H1R0WB1D", "lcsc": false+ },+ "marking": {+ "text": null,+ "statement": "The Murata reference sheet specifies no marking for this part: none of its 29 pages has a marking section or mentions a part marking, and the p.2 dimension sketch shows a plain body",+ "source": "GJM1555C1H1R0WB01-01A, all 29 pages searched; p.2" } } }
provenance.json+54−60@@ -11,69 +11,63 @@ "exit": 0 }, "files": {- "README.md": "3a0ababf1f2ace274b2582d9fc89005038ddce2fa215f73a36ebdc44a811734e",+ "README.md": "5279e91f7ec32ae58e676eb3a702cffb53259d4ffb01f8d5f5bb3b299ab3fb33", "component.kicad_mod": "0403382fc4583ed510b461b1fa4a36dfaec6f4c0d9b1a67e6b0027837a54e1b5", "component.kicad_sym": "5a65a4639e0f2c0ddb5de37f0a551346c10526844927bc902c6badc4d0d2b059",+ "docs/gjm1555c1h1r0wb01d-datasheet.pdf": "b3ecfddddfd268d8ef97b7c3a3085de92738a9a6bf995d9b1941f634663ea470",+ "docs/verify/gjm1555c1h1r0wb01d-overlay.png": "9c1d108910c74d7f010619e3e2d91d2d56b18ba2d27196d0679ccc23de25e93a",+ "lod/high.glb": "68acbdcb91d8e05c66efc501487869191d7f8d9109a28a9783d28651d4b45dac",+ "lod/low.glb": "97c28a2b6729215f6ac551885121829bfecd90f647b98917dda123fd5dca1c29",+ "lod/medium.glb": "4472dbe7df7a96447c93f320a683313eeee31981de733eaa9dd185ea0d0422a2",+ "lod/super-low.glb": "f43ec4fa773604481f92061a583e06c0a643e4fd35ec0d9b2ebe1979f7ba50f9",+ "models.json": "08fd433679062ecb0c6c3ec5e390b9eab7abc9f82b61b8438959d9daca9d834a", "package-reference.glb": "100935c5c0d6f20a6c9bdfb6386f874dc8a4457bf0e4f3ace69f1ab090100773", "package-reference.step": "03e44c4b2b727b8d4b6b1fd598cbd12dd5e614f1dfeefa96a92a07e3a3b312a2",- "package.json": "78149e98f3f9a3898a23bfe8ef214fb1825832c537b7163e52e585b5cf9a60cc",- "page.json": "f264bc4e4fc62195274bfaf18075ee4ba43dfc89007576bd5ee63e295b42afbd",- "gjm1555c1h1r0wb01d-mpn.step": "6329e4a8a041a01ce2ea55cfffee867ed47519bea76ea04a857211b188d3ffce",- "gjm1555c1h1r0wb01d-mpn.glb": "77700f5a864ca256bf64653134649c826cc38eb26e8ae79e8d644680b5f05550",- "models.json": "07ef7dcab0d48c2ea20381f90db6bab2493d29574322b24522327386ebd2c41d",- "docs/gjm1555c1h1r0wb01d-mpn.check.png": "8bf945dbb51c23d42c32af75dc62d0351dcf362110d9c9b19161ec7b0ad4206e"+ "package.json": "a83f5a1dbd5a8c84fa61d70d412f8e1df98811399b734f88e3a017c4ed4fff2f",+ "page.json": "fbcee2e6ac311cba98ba026434607ed78cafb9aae793ba8140e2243f74428b7c",+ "verification.json": "b96467e880a61965a4b8d241629c6fac5ddc770c964453bf3850e5b3923ad555" },- "mpn_marked_model": {- "file": "gjm1555c1h1r0wb01d-mpn.step",- "sha256": "6329e4a8a041a01ce2ea55cfffee867ed47519bea76ea04a857211b188d3ffce",- "glb": "gjm1555c1h1r0wb01d-mpn.glb",- "glb_sha256": "77700f5a864ca256bf64653134649c826cc38eb26e8ae79e8d644680b5f05550",- "classification": "AI-created derived file. The geometry is the unchanged KiCad package model (package-reference.step); the laser-etch style MPN text is a virtual identification mark added by AI tooling, not a claim about the manufacturer's real marking (passives of this size are normally unmarked).",- "created_by": "Claude (AI) for John Lauer, 2026-10-19",- "source": {- "file": "package-reference.step",- "sha256": "03e44c4b2b727b8d4b6b1fd598cbd12dd5e614f1dfeefa96a92a07e3a3b312a2",- "footprint": "component.kicad_mod",- "footprint_sha256": "0403382fc4583ed510b461b1fa4a36dfaec6f4c0d9b1a67e6b0027837a54e1b5"- },- "tool": {- "etch": "adom-aiflow-etch (adom-aiflow 0.2.4)",- "service": "service-step2glb v1.9.3 reachable at https://step2glb-gmdoncpxdwx0.adom.cloud (kicad_base=https://kicad-rk5ue5pcfemi.adom.cloud, converts=2161, uptime=294250s)",- "glb": "step2glb 0.13.2 (step2glb convert gjm1555c1h1r0wb01d-mpn.step -o gjm1555c1h1r0wb01d-mpn.glb)"- },- "parameters": {- "command": "adom-aiflow-etch --in package-reference.step --mpn GJM1555C1H1R0WB01D --out gjm1555c1h1r0wb01d-mpn.step --footprint component.kicad_mod",- "mpn": "GJM1555C1H1R0WB01D",- "lines": [- "GJM1555C1",- "H1R0WB01D"- ],- "fit": {- "scale": 0.8572,- "capMm": 0.0619,- "markMm": [- 0.456,- 0.1457- ],- "bandMm": [- 0.6,- 0.42- ],- "zMm": 0.485+ "marking": {+ "real_marking": null,+ "statement": "The Murata reference sheet specifies no marking for this part: none of its 29 pages has a marking section or mentions a part marking, and the p.2 dimension sketch shows a plain body",+ "source": "GJM1555C1H1R0WB01-01A, all 29 pages searched; p.2"+ },+ "mpn_marked_model_removed": {+ "date": "2026-10-19",+ "files": [+ {+ "path": "gjm1555c1h1r0wb01d-mpn.step",+ "sha256": "6329e4a8a041a01ce2ea55cfffee867ed47519bea76ea04a857211b188d3ffce"+ },+ {+ "path": "gjm1555c1h1r0wb01d-mpn.glb",+ "sha256": "77700f5a864ca256bf64653134649c826cc38eb26e8ae79e8d644680b5f05550" },- "plan_summary": "Mark along the 0.60 mm axis (+X), 0.06 mm text (2 lines), upright with pin 1 left middle, outline rot 180 after a 180 deg pose turn; ~14 px caps at 320 px",- "mark": "light, flat, coplanar on the top face (Adom.LaserEtch)"- },- "check": {- "command": "adom-aiflow-etch check gjm1555c1h1r0wb01d-mpn.step --footprint component.kicad_mod",- "status": "PASS",- "hints": [- "Hint: Mark along 0.60 mm axis, 0.06 mm text, upright with pin 1 left middle: PASS",- "Hint: frame: normalized pose (source -> normalized: identity, as-is (Z up); normalized -> check image: identity, -> outline: 180 deg about Z); model up inferred, face top inferred, text up inferred from pin 1; not a mounted-pose check",- "Hint: PASS. Look at /tmp/claude-1001/-home-adom-project/43add640-14a6-413a-b15b-ffdadeb7cf16/scratchpad/work/gjm1555c1h1r0wb01d/gjm1555c1h1r0wb01d-mpn.check.png (top view) and at the outline at widget size before you report."- ],- "image": "docs/gjm1555c1h1r0wb01d-mpn.check.png",- "looked_at": "yes, top view: along the body between the end caps, upright, reads left to right"- }- }-}+ {+ "path": "docs/gjm1555c1h1r0wb01d-mpn.check.png",+ "sha256": "8bf945dbb51c23d42c32af75dc62d0351dcf362110d9c9b19161ec7b0ad4206e"+ }+ ],+ "reason": "John's rule: every chip shows only its real laser marking from the manufacturer's datasheet. The Murata reference sheet specifies no marking for this part: none of its 29 pages has a marking section or mentions a part marking, and the p.2 dimension sketch shows a plain body (GJM1555C1H1R0WB01-01A, all 29 pages searched; p.2), so the AI-added MPN etch misrepresented the part and was deleted.",+ "removed_by": "Claude (AI agent) for John Lauer"+ },+ "datasheet": {+ "file": "docs/gjm1555c1h1r0wb01d-datasheet.pdf",+ "sha256": "b3ecfddddfd268d8ef97b7c3a3085de92738a9a6bf995d9b1941f634663ea470",+ "url": "https://search.murata.co.jp/Ceramy/image/img/A01X/G101/ENG/GJM1555C1H1R0WB01-01A.pdf",+ "title": "Murata reference sheet GJM1555C1H1R0WB01-01A, specifications as of Apr. 15, 2026",+ "retrieved": "2026-10-19"+ },+ "datasheet_check": {+ "overlay": "docs/verify/gjm1555c1h1r0wb01d-overlay.png",+ "numbers": "docs/verify/gjm1555c1h1r0wb01d-check.json",+ "tools": "provenance/tools/ (ortho.py, overlay_chip.py, spec json)",+ "result": "PASS"+ },+ "lod": {+ "manifest": "lod/manifest.json",+ "generator": "provenance/tools/lod.py",+ "source_sha256": "03e44c4b2b727b8d4b6b1fd598cbd12dd5e614f1dfeefa96a92a07e3a3b312a2"+ },+ "files_note": "page.json, package.json and README.md hashes are as written before this provenance file; provenance.json itself is not listed."+}
provenance/tools/gjm1555c1h1r0wb01d-overlay-spec.jsonadded+39@@ -0,0 +1,39 @@+{+ "label": "GJM1555C1H1R0WB01D",+ "mpn": "GJM1555C1H1R0WB01D",+ "source": "Murata GJM1555C1H1R0WB01-01A reference sheet p.2 'Type & Dimension' (1005M/0402)",+ "L": [+ 1.0,+ 0.05+ ],+ "W": [+ 0.5,+ 0.05+ ],+ "T": [+ 0.5,+ 0.05+ ],+ "band_top": [+ 0.15,+ 0.35+ ],+ "band_bot": [+ 0.15,+ 0.35+ ],+ "band_top_name": "e (terminal band), top",+ "band_bot_name": "e (terminal band), bottom",+ "crops": [+ [+ "p-02.png",+ [+ 111,+ 462,+ 1030,+ 895+ ]+ ]+ ],+ "marking": "none specified anywhere in the 29/33-page sheet (no marking section)"+}
provenance/tools/lod.pyadded+170@@ -0,0 +1,170 @@+"""Board-use LOD GLBs (high, medium, low, super-low) for a small two-terminal chip.++Input: the page's board-use STEP (Z-up, mm, seated on z=0, centred).+Output (in <outdir>/lod/): high.glb medium.glb low.glb super-low.glb, a 400x300+PNG preview of each rendered from that GLB's own triangles, manifest.json+(schema adom/component-lod@1).++Every level: Z-up, metres, the same origin and pose as the STEP (no recentring,+no rotation), primitives batched by material (one per material), the STEP's own+colours. No footprint, pad outline, label or silkscreen helpers exist in the+source, so nothing is removed. high/medium/low are OCCT tessellations of the+exact B-rep at decreasing deflection; super-low replaces each material group by+its bounding box per side of the part (terminals, body, overcoat), which keeps+the silhouette, the terminal bands and the seating plane.++usage: lod.py part.step MPN outdir [source_label]+"""+import sys, os, json, struct, hashlib+import numpy as np+from PIL import Image, ImageDraw+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))+import ortho+from OCP.BRepMesh import BRepMesh_IncrementalMesh+from OCP.BRepTools import BRepTools++step, mpn, outdir = sys.argv[1], sys.argv[2], sys.argv[3]+src_label = sys.argv[4] if len(sys.argv) > 4 else os.path.basename(step)+lod_dir = os.path.join(outdir, "lod"); os.makedirs(lod_dir, exist_ok=True)++LEVELS = [("high", 0.0005, 0.1), ("medium", 0.004, 0.35), ("low", 0.02, 0.9)]++def tris_at(lin, ang):+ # ortho.load_tris meshes with fixed params; clean and remesh by patching BRepMesh call+ orig = ortho.BRepMesh_IncrementalMesh+ def mesh(shp, _l, rel, _a, par):+ BRepTools.Clean_s(shp)+ return orig(shp, lin, False, ang, True)+ ortho.BRepMesh_IncrementalMesh = mesh+ try:+ return ortho.load_tris(step)+ finally:+ ortho.BRepMesh_IncrementalMesh = orig++def boxes_from(tris):+ groups = {}+ for p, c in tris:+ groups.setdefault(c, []).append(p)+ allp = np.concatenate([p for p, _ in tris]); xmax = allp[:, 0].max()+ endc = {}+ for p, c in tris:+ if np.allclose(p[:, 0], xmax, atol=1e-4): endc[c] = endc.get(c, 0) + 1+ tcol = max(endc, key=endc.get)+ boxes = []+ for c, ps in groups.items():+ T = np.stack(ps)+ crosses = ((T[:, :, 0].min(1) < -1e-6) & (T[:, :, 0].max(1) > 1e-6)).any()+ P = T.reshape(-1, 3)+ parts = [P] if crosses else [P[P[:, 0] < 0], P[P[:, 0] > 0]]+ for Q in parts:+ if len(Q):+ boxes.append([Q.min(0), Q.max(0), c])+ tin = min(min(abs(lo[0]), abs(hi[0])) for lo, hi, c in boxes if c == tcol)+ out = []+ for lo, hi, c in boxes:+ if c != tcol:+ lo = lo.copy(); hi = hi.copy(); lo[0] = max(lo[0], -tin); hi[0] = min(hi[0], tin)+ v = np.array([[x, y, z] for x in (lo[0], hi[0]) for y in (lo[1], hi[1]) for z in (lo[2], hi[2])])+ for a_, b_, cc, d in [(0, 1, 3, 2), (4, 6, 7, 5), (0, 4, 5, 1), (2, 3, 7, 6), (0, 2, 6, 4), (1, 5, 7, 3)]:+ out.append((v[[a_, b_, cc]], c)); out.append((v[[a_, cc, d]], c))+ return out++def write_glb(tris, path, name):+ mats = []; groups = {}+ for p, c in tris:+ if c not in groups:+ groups[c] = []; mats.append(c)+ groups[c].append(p)+ bin_ = b""; views = []; accs = []; prims = []+ def add_view(data, target):+ nonlocal bin_+ off = len(bin_); bin_ += data+ while len(bin_) % 4: bin_ += b"\0"+ views.append({"buffer": 0, "byteOffset": off, "byteLength": len(data), "target": target})+ return len(views) - 1+ decoded = 0; ntri = 0+ for mi, c in enumerate(mats):+ P = np.concatenate(groups[c]).reshape(-1, 3, 3)+ n = np.cross(P[:, 1] - P[:, 0], P[:, 2] - P[:, 0]); n /= np.linalg.norm(n, axis=1, keepdims=True) + 1e-30+ pos = (P.reshape(-1, 3) / 1000.0).astype(np.float32) # mm -> metres+ nor = np.repeat(n, 3, axis=0).astype(np.float32)+ # weld identical (position, normal) vertices+ key = np.concatenate([np.round(pos, 9), np.round(nor, 5)], 1)+ uniq, inv = np.unique(key, axis=0, return_inverse=True)+ upos = uniq[:, :3].astype(np.float32); unor = uniq[:, 3:].astype(np.float32)+ idx = inv.astype(np.uint16 if len(uniq) < 65535 else np.uint32).ravel()+ vp = add_view(upos.tobytes(), 34962); vn = add_view(unor.tobytes(), 34962); vi = add_view(idx.tobytes(), 34963)+ accs.append({"bufferView": vp, "componentType": 5126, "count": len(upos), "type": "VEC3",+ "min": upos.min(0).tolist(), "max": upos.max(0).tolist()})+ accs.append({"bufferView": vn, "componentType": 5126, "count": len(unor), "type": "VEC3"})+ accs.append({"bufferView": vi, "componentType": 5123 if idx.dtype == np.uint16 else 5125, "count": len(idx), "type": "SCALAR"})+ prims.append({"attributes": {"POSITION": len(accs) - 3, "NORMAL": len(accs) - 2}, "indices": len(accs) - 1, "material": mi})+ decoded += upos.nbytes + unor.nbytes + idx.nbytes; ntri += len(idx) // 3+ gl = {"asset": {"version": "2.0", "generator": "capencoder passives lod.py (OCCT tessellation)"},+ "scene": 0, "scenes": [{"nodes": [0]}],+ "nodes": [{"name": name, "mesh": 0, "extras": {"adomUpAxis": "Z", "units": "metres"}}],+ "meshes": [{"name": name, "primitives": prims}],+ "materials": [{"name": f"mat_{i}", "pbrMetallicRoughness": {"baseColorFactor": [*c, 1.0], "metallicFactor": 0.0, "roughnessFactor": 0.6}} for i, c in enumerate(mats)],+ "accessors": accs, "bufferViews": views, "buffers": [{"byteLength": len(bin_)}]}+ js = json.dumps(gl, separators=(",", ":")).encode()+ while len(js) % 4: js += b" "+ glb = struct.pack("<III", 0x46546C67, 2, 12 + 8 + len(js) + 8 + len(bin_)) + struct.pack("<II", len(js), 0x4E4F534A) + js + struct.pack("<II", len(bin_), 0x004E4942) + bin_+ open(path, "wb").write(glb)+ return {"decodedBufferBytes": decoded, "triangles": ntri, "primitives": len(prims)}++def read_glb_tris(path):+ b = open(path, "rb").read(); n = struct.unpack("<I", b[12:16])[0]+ j = json.loads(b[20:20 + n]); binc = b[20 + n + 8:]+ def acc(i, dt, w):+ a = j["accessors"][i]; v = j["bufferViews"][a["bufferView"]]+ return np.frombuffer(binc, dt, a["count"] * w, v["byteOffset"]).reshape(-1, w) if w > 1 else np.frombuffer(binc, dt, a["count"], v["byteOffset"])+ out = []+ for p in j["meshes"][0]["primitives"]:+ pos = acc(p["attributes"]["POSITION"], np.float32, 3) * 1000.0+ ia = j["accessors"][p["indices"]]+ idx = acc(p["indices"], np.uint16 if ia["componentType"] == 5123 else np.uint32, 1)+ col = tuple(j["materials"][p["material"]]["pbrMetallicRoughness"]["baseColorFactor"][:3])+ for t in idx.reshape(-1, 3):+ out.append((pos[t].astype(float), col))+ return out++ortho.VIEWS["iso"] = (tuple(np.array([0.7071, -0.7071, 0])), tuple(np.array([-0.4082, -0.4082, 0.8165])), tuple(np.array([0.5774, 0.5774, 0.5774])))+def preview(path, png):+ tris = read_glb_tris(path)+ allp = np.concatenate([t[0] for t in tris]); ext = np.abs(allp).max()+ img = ortho.render(tris, "iso", 300 / (3.3 * ext), size=(400, 300))+ bg = Image.new("RGBA", img.size, (48, 57, 68, 255)); bg.alpha_composite(img); bg.convert("RGB").save(png)++sha = lambda f: hashlib.sha256(open(f, "rb").read()).hexdigest()+levels = []+src_tris = None+for name, lin, ang in LEVELS + [("super-low", None, None)]:+ if name == "super-low":+ tris = boxes_from(src_tris)+ else:+ tris = tris_at(lin, ang)+ if name == "high":+ src_tris = tris+ path = os.path.join(lod_dir, f"{name}.glb")+ st = write_glb(tris, path, mpn)+ back = read_glb_tris(path); P = np.concatenate([t[0] for t in back])+ png = os.path.join(lod_dir, f"{name}.png"); preview(path, png)+ rec = {"level": name, "path": f"lod/{name}.glb", "sha256": sha(path), "sizeBytes": os.path.getsize(path), **st,+ "bounds": {"min": (P.min(0) / 1000).round(7).tolist(), "max": (P.max(0) / 1000).round(7).tolist()},+ "approximation": ({"method": "OCCT BRepMesh_IncrementalMesh", "linearDeflectionMm": lin, "angularDeflectionRad": ang}+ if lin else {"method": "per-material bounding boxes split at the part centre (terminals, body, overcoat)"}),+ "detailVisibility": {"virtualMpn": False, "note": "no marking: the datasheet specifies none"},+ "review": "Candidate: rendered preview checked by AI (silhouette, terminals, seat z=0); not human-reviewed",+ "preview": {"path": f"lod/{name}.png", "sha256": sha(png), "sizeBytes": os.path.getsize(png), "width": 400, "height": 300,+ "view": "iso", "sourceLevelSha256": sha(path)}}+ levels.append(rec)+ print(name, st, rec["bounds"])+manifest = {"schema": "adom/component-lod@1", "mpn": mpn, "upAxis": "Z", "units": "metres",+ "status": "candidate (AI-checked, not human-reviewed)",+ "purpose": "clean board-use component models: no footprint pads, outlines, labels or silkscreen; no marking (none per datasheet)",+ "source": {"path": src_label, "sha256": sha(step)},+ "sourceToDisplay": "uniform scale 0.001 (mm to m); no rotation, no translation; origin = footprint origin, seat z = 0",+ "generator": "lod.py (capencoder passives, OCCT via cadquery-ocp)",+ "removedHelpers": [], "levels": levels,+ "hints": ["All four levels share pose and units; super-low is boxes per material group, chosen for board overviews."]}+json.dump(manifest, open(os.path.join(lod_dir, "manifest.json"), "w"), indent=1)
provenance/tools/ortho.pyadded+123@@ -0,0 +1,123 @@+"""Orthographic renderer for small component STEPs (face colours kept).++render(step, view, ppmm) -> PIL RGBA image, model origin at the image centre.+views: top (look down -Z, +X right, +Y up), bottom (look up +Z, +X right, +Y down,+i.e. the part flipped about X), front (look +Y, +X right, +Z up),+end (look -X, +Y right... from +X end: -Y right, +Z up).+"""+import sys+import numpy as np+from PIL import Image, ImageDraw+from OCP.STEPCAFControl import STEPCAFControl_Reader+from OCP.TDocStd import TDocStd_Document+from OCP.TCollection import TCollection_ExtendedString+from OCP.XCAFDoc import XCAFDoc_DocumentTool, XCAFDoc_ColorType+from OCP.TDF import TDF_LabelSequence+from OCP.TopExp import TopExp_Explorer+from OCP.TopAbs import TopAbs_FACE, TopAbs_REVERSED+from OCP.TopoDS import TopoDS+from OCP.Quantity import Quantity_Color+from OCP.BRepMesh import BRepMesh_IncrementalMesh+from OCP.BRep import BRep_Tool+from OCP.TopLoc import TopLoc_Location+++def load_tris(path):+ doc = TDocStd_Document(TCollection_ExtendedString("x"))+ r = STEPCAFControl_Reader(); r.SetColorMode(True); r.SetNameMode(True)+ r.ReadFile(path); r.Transfer(doc)+ st = XCAFDoc_DocumentTool.ShapeTool_s(doc.Main())+ ct = XCAFDoc_DocumentTool.ColorTool_s(doc.Main())+ labs = TDF_LabelSequence(); st.GetFreeShapes(labs)+ out = []+ leaves = []+ def walk(lab, loc, pcol):+ c = Quantity_Color(); col = pcol+ for t in (XCAFDoc_ColorType.XCAFDoc_ColorSurf, XCAFDoc_ColorType.XCAFDoc_ColorGen):+ if ct.GetColor_s(lab, t, c):+ col = (c.Red(), c.Green(), c.Blue()); break+ if st.IsReference_s(lab):+ ref = type(lab)(); st.GetReferredShape_s(lab, ref)+ walk(ref, loc.Multiplied(st.GetLocation_s(lab)), col); return+ kids = TDF_LabelSequence(); st.GetComponents_s(lab, kids)+ if kids.Length() > 0:+ for i in range(1, kids.Length() + 1): walk(kids.Value(i), loc, col)+ return+ leaves.append((st.GetShape_s(lab).Moved(loc), col))+ for li in range(1, labs.Length() + 1):+ walk(labs.Value(li), TopLoc_Location(), None)+ for shp, pcol in leaves:+ BRepMesh_IncrementalMesh(shp, 0.002, False, 0.2, True)+ e = TopExp_Explorer(shp, TopAbs_FACE)+ while e.More():+ f = TopoDS.Face_s(e.Current())+ c = Quantity_Color(); col = None+ for t in (XCAFDoc_ColorType.XCAFDoc_ColorSurf, XCAFDoc_ColorType.XCAFDoc_ColorGen):+ if ct.GetColor(f, t, c):+ col = (c.Red(), c.Green(), c.Blue()); break+ if col is None:+ col = pcol or (0.6, 0.6, 0.6)+ loc = TopLoc_Location()+ tri = BRep_Tool.Triangulation_s(f, loc)+ if tri is not None:+ trsf = loc.Transformation()+ pts = np.array([(lambda p: (p.X(), p.Y(), p.Z()))(tri.Node(i).Transformed(trsf)) for i in range(1, tri.NbNodes() + 1)])+ rev = f.Orientation() == TopAbs_REVERSED+ for i in range(1, tri.NbTriangles() + 1):+ a, b, cc = tri.Triangle(i).Get()+ if rev:+ b, cc = cc, b+ out.append((pts[[a - 1, b - 1, cc - 1]], col))+ e.Next()+ return out+++VIEWS = {+ # (right axis vector, up axis vector, toward-viewer vector)+ "top": ((1, 0, 0), (0, 1, 0), (0, 0, 1)),+ "bottom": ((1, 0, 0), (0, -1, 0), (0, 0, -1)),+ "front": ((1, 0, 0), (0, 0, 1), (0, -1, 0)),+ "end": ((0, -1, 0), (0, 0, 1), (1, 0, 0)),+}+++def render(tris, view, ppmm, size=None, shade=True, ss=3):+ R, U, V = [np.array(v, float) for v in VIEWS[view]]+ allp = np.concatenate([t[0] for t in tris])+ ext = max(np.abs(allp @ R).max(), np.abs(allp @ U).max())+ if size is None:+ size = (int(2 * ext * ppmm + 40), int(2 * ext * ppmm + 40))+ W, H = size[0] * ss, size[1] * ss+ img = Image.new("RGBA", (W, H), (0, 0, 0, 0))+ d = ImageDraw.Draw(img)+ items = []+ light = np.array([0.3, 0.4, 0.85]); light /= np.linalg.norm(light)+ for p, col in tris:+ n = np.cross(p[1] - p[0], p[2] - p[0])+ nn = np.linalg.norm(n)+ if nn == 0:+ continue+ n /= nn+ if n @ V <= 1e-6:+ continue # back face+ depth = (p @ V).mean()+ lv = 0.55 + 0.45 * abs(n @ (R * light[0] + U * light[1] + V * light[2])) if shade else 1.0+ rgb = tuple(int(255 * min(1, c * lv)) for c in col)+ xy = [(W / 2 + (q @ R) * ppmm * ss, H / 2 - (q @ U) * ppmm * ss) for q in p]+ items.append((depth, xy, rgb))+ items.sort(key=lambda x: x[0])+ for _, xy, rgb in items:+ d.polygon(xy, fill=rgb + (255,), outline=rgb + (255,))+ return img.resize(size, Image.LANCZOS)+++if __name__ == "__main__":+ tris = load_tris(sys.argv[1])+ out = sys.argv[2]+ ppmm = float(sys.argv[3]) if len(sys.argv) > 3 else 300+ imgs = [render(tris, v, ppmm) for v in ("top", "bottom", "front", "end")]+ w = sum(i.width for i in imgs); h = max(i.height for i in imgs)+ sheet = Image.new("RGBA", (w, h), (255, 255, 255, 255)); x = 0+ for i in imgs:+ sheet.alpha_composite(i, (x, 0)); x += i.width+ sheet.save(out)
provenance/tools/overlay_chip.pyadded+146@@ -0,0 +1,146 @@+"""Prove a two-terminal chip model (MLCC / chip resistor) against its datasheet table.++The maker prints a dimension table with an unscaled sketch, so the overlay is made+against a true-scale drawing generated from the printed table (300 px/mm):+nominal outline solid black, tolerance limits dashed blue, the allowed range of+the terminal inner edge shaded amber. The model is rendered at the same scale+(50 % fill, red outline) on top. The numeric check measures the STEP itself.++usage: overlay_chip.py spec.json model.step out.png+spec: {"label","mpn","source","L":[nom,tol],"W":[nom,tol],"T":[nom,tol],+ "band_top":[min,max],"band_bot":[min,max],"crops":[[png,[x0,y0,x1,y1]],...],+ "marking": "..."}+"""+import sys, json+import numpy as np+from PIL import Image, ImageDraw, ImageFont, ImageFilter+sys.path.insert(0, __file__.rsplit("/", 1)[0])+from ortho import load_tris, render+from OCP.STEPControl import STEPControl_Reader+from OCP.TopExp import TopExp_Explorer+from OCP.TopAbs import TopAbs_FACE+from OCP.TopoDS import TopoDS+from OCP.Bnd import Bnd_Box+from OCP.BRepBndLib import BRepBndLib+from OCP.BRepAdaptor import BRepAdaptor_Surface+from OCP.GeomAbs import GeomAbs_Plane++PP = 300.0+spec = json.load(open(sys.argv[1])); step = sys.argv[2]; out = sys.argv[3]+F = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"+font = ImageFont.truetype(F, 22); fsm = ImageFont.truetype(F, 18); fbig = ImageFont.truetype(F, 26)++# ---------- numeric measurement of the STEP ----------+tris = load_tris(step)+allp = np.concatenate([t[0] for t in tris])+x0, y0, z0 = allp.min(0); x1, y1, z1 = allp.max(0)+Lm, Wm, Tm = x1 - x0, y1 - y0, z1 - z0+# terminal colour = colour of triangles lying on the +X end face+endcols = {}+for p, c in tris:+ if np.allclose(p[:, 0], x1, atol=1e-4):+ endcols[c] = endcols.get(c, 0) + 1+tcol = max(endcols, key=endcols.get)+def band(zlev):+ xs = [p[:, 0] for p, c in tris if c == tcol and np.allclose(p[:, 2], zlev, atol=1e-4)]+ if not xs:+ return None+ xs = np.concatenate(xs)+ pos = xs[xs > 0]; neg = xs[xs < 0]+ return round(min(x1 - pos.min(), -x0 + neg.max() if len(neg) else 9), 3)+bt, bb = band(z1), band(z0)++def rng(v):+ return (v[0] - v[1], v[0] + v[1]) if len(v) == 2 and v[1] < v[0] else tuple(v)+checks = []+def chk(name, val, lo, hi):+ ok = val is not None and lo - 1e-6 <= val <= hi + 1e-6+ checks.append((name, val, lo, hi, ok))+chk("L", round(Lm, 3), *rng(spec["L"]))+chk("W", round(Wm, 3), *rng(spec["W"]))+chk("T/H", round(Tm, 3), *rng(spec["T"]))+chk(spec.get("band_top_name", "terminal band, top"), bt, *spec["band_top"])+chk(spec.get("band_bot_name", "terminal band, bottom"), bb, *spec["band_bot"])+centred = bool(abs(x0 + x1) < 1e-3 and abs(y0 + y1) < 1e-3 and abs(z0) < 1e-3)+longx = bool(Lm > Wm)++# ---------- true-scale drawing + model per view ----------+def view_panel(view, a_len, a_tol, b_len, b_tol, bands, title):+ im = render(tris, view, PP, size=(560, 420))+ W_, H_ = im.size; cx = W_ / 2+ panel = Image.new("RGBA", (W_, H_), (255, 255, 255, 255))+ d = ImageDraw.Draw(panel)+ seat = view in ("front", "end")+ def rect(a, b, **kw):+ # a along horizontal, b along vertical; for side views the box sits on the seat line+ if seat:+ return [cx - a * PP / 2, H_ / 2 - b * PP, cx + a * PP / 2, H_ / 2]+ return [cx - a * PP / 2, H_ / 2 - b * PP / 2, cx + a * PP / 2, H_ / 2 + b * PP / 2]+ # allowed inner-edge range for the terminals (relative to nominal length)+ for (lo, hi, where) in bands:+ for s in (-1, 1):+ xa = cx + s * (a_len / 2 - hi) * PP; xb = cx + s * (a_len / 2 - lo) * PP+ if where == "full":+ ya, yb = rect(a_len, b_len)[1], rect(a_len, b_len)[3]+ elif where == "top":+ r_ = rect(a_len, b_len); ya, yb = r_[1] - 12, r_[1] + 12+ else:+ r_ = rect(a_len, b_len); ya, yb = r_[3] - 12, r_[3] + 12+ d.rectangle([min(xa, xb), ya, max(xa, xb), yb], fill=(255, 196, 80, 255))+ def dashed(box, col):+ xa, ya, xb, yb = box+ for (p, q) in (((xa, ya), (xb, ya)), ((xb, ya), (xb, yb)), ((xb, yb), (xa, yb)), ((xa, yb), (xa, ya))):+ n = int(max(abs(q[0] - p[0]), abs(q[1] - p[1])) / 8) or 1+ for i in range(0, n, 2):+ t0, t1 = i / n, min(1, (i + 1) / n)+ d.line([(p[0] + (q[0] - p[0]) * t0, p[1] + (q[1] - p[1]) * t0),+ (p[0] + (q[0] - p[0]) * t1, p[1] + (q[1] - p[1]) * t1)], fill=col, width=2)+ dashed(rect(a_len - a_tol, b_len - b_tol), (30, 90, 220))+ dashed(rect(a_len + a_tol, b_len + b_tol), (30, 90, 220))+ d.rectangle(rect(a_len, b_len), outline=(0, 0, 0), width=2)+ arr = np.asarray(im).copy(); alpha = arr[:, :, 3].copy(); arr[:, :, 3] = (alpha * 0.5).astype(np.uint8)+ panel.alpha_composite(Image.fromarray(arr))+ m = Image.fromarray(((alpha > 128) * 255).astype(np.uint8))+ edge = (np.asarray(m, int) - np.asarray(m.filter(ImageFilter.MinFilter(3)), int)) > 0+ red = np.zeros((H_, W_, 4), np.uint8); red[edge] = (220, 30, 30, 255)+ panel.alpha_composite(Image.fromarray(red))+ d = ImageDraw.Draw(panel); d.text((10, 8), title, fill=(0, 0, 0), font=fsm)+ return panel++L, W, T = spec["L"], spec["W"], spec["T"]+bt_r, bb_r = spec["band_top"], spec["band_bot"]+panels = [+ view_panel("top", L[0], L[1], W[0], W[1], [(bt_r[0], bt_r[1], "full")], "TOP (looking down, X right)"),+ view_panel("bottom", L[0], L[1], W[0], W[1], [(bb_r[0], bb_r[1], "full")], "BOTTOM (flipped about X)"),+ view_panel("front", L[0], L[1], T[0], T[1], [(bt_r[0], bt_r[1], "top"), (bb_r[0], bb_r[1], "bot")], "SIDE (looking +Y)"),+ view_panel("end", W[0], W[1], T[0], T[1], [], "END (looking -X)"),+]+pw, ph = panels[0].size+sheet_w = pw * 4 + 50+crops = [Image.open(p).convert("RGB").crop(tuple(b)) for p, b in spec["crops"]]+crop_h = 0; scaled = []+for c in crops:+ s = min(1.0, (sheet_w - 40) / c.width, 420 / c.height)+ c2 = c.resize((int(c.width * s), int(c.height * s)), Image.LANCZOS); scaled.append(c2); crop_h += c2.height + 20+text_h = 120 + 30 * len(checks) + 90+sheet = Image.new("RGB", (sheet_w, 80 + ph + text_h + crop_h), (255, 255, 255))+d = ImageDraw.Draw(sheet)+d.text((20, 15), f"{spec['label']}: model vs datasheet table, true scale {int(PP)} px/mm", fill=(0, 0, 0), font=fbig)+d.text((20, 50), f"Model: {step.rsplit('/', 1)[-1]} Datasheet: {spec['source']}", fill=(70, 70, 70), font=fsm)+for i, p in enumerate(panels):+ sheet.paste(p.convert("RGB"), (10 + i * (pw + 10), 80))+y = 90 + ph+d.text((20, y), "Legend: black = printed nominal, blue dashed = printed tolerance limits, amber = allowed position of the terminal inner edge, red outline + 50% fill = the 3D model.", fill=(0, 0, 0), font=fsm); y += 34+d.text((20, y), "Measured on the STEP value datasheet range result", fill=(0, 0, 0), font=font); y += 32+for name, val, lo, hi, ok in checks:+ d.text((20, y), f"{name:<34}{'-' if val is None else f'{val:.3f}':>8} {lo:.2f} to {hi:.2f} {'PASS' if ok else 'FAIL'}",+ fill=(0, 120, 40) if ok else (200, 0, 0), font=font); y += 30+d.text((20, y + 4), f"Frame: Z-up, seated z=0, centred: {'yes' if centred else 'NO'}; long axis on X: {'yes' if longx else 'NO'}. Marking: {spec['marking']}", fill=(0, 0, 0), font=fsm)+y += 50+for c in scaled:+ sheet.paste(c, (20, y)); d.rectangle([20, y, 20 + c.width, y + c.height], outline=(150, 150, 150)); y += c.height + 20+sheet.save(out)+res = {"step": step, "bbox_mm": [round(float(v), 4) for v in (x0, x1, y0, y1, z0, z1)],+ "checks": [{"dim": n, "model_mm": v, "datasheet_min": lo, "datasheet_max": hi, "pass": bool(ok)} for n, v, lo, hi, ok in checks],+ "z_up_seated_centred": bool(centred), "long_axis_x": bool(longx), "all_pass": bool(all(c[4] for c in checks) and centred and longx)}+print(json.dumps(res))
verification.jsonadded+147@@ -0,0 +1,147 @@+{+ "schema": "adom/verification@1",+ "part": "GJM1555C1H1R0WB01D",+ "variants": [],+ "entries": [+ {+ "date": "2026-10-19",+ "by": {+ "name": "Claude (AI agent) for John Lauer",+ "handle": "john"+ },+ "status": "verified",+ "title": "3D model matches the datasheet package dimensions",+ "scope": [+ "3d-model"+ ],+ "variants": [+ "*"+ ],+ "checks": [+ {+ "text": "L: model 1.000 mm, datasheet 0.95 to 1.05 mm",+ "result": "pass"+ },+ {+ "text": "W: model 0.500 mm, datasheet 0.45 to 0.55 mm",+ "result": "pass"+ },+ {+ "text": "T/H: model 0.500 mm, datasheet 0.45 to 0.55 mm",+ "result": "pass"+ },+ {+ "text": "e (terminal band), top: model 0.200 mm, datasheet 0.15 to 0.35 mm",+ "result": "pass"+ },+ {+ "text": "e (terminal band), bottom: model 0.200 mm, datasheet 0.15 to 0.35 mm",+ "result": "pass"+ },+ {+ "text": "Z-up, seated on z = 0, centred on the footprint origin, long axis on X",+ "result": "pass"+ },+ {+ "text": "the maker's sketch is not to scale; the overlay is against a true-scale drawing of the printed table",+ "result": "note"+ }+ ],+ "evidence": [+ {+ "label": "overlay (top, bottom, side, end at 300 px/mm)",+ "href": "docs/verify/gjm1555c1h1r0wb01d-overlay.png"+ },+ {+ "label": "numbers",+ "href": "docs/verify/gjm1555c1h1r0wb01d-check.json"+ },+ {+ "label": "datasheet (Murata reference sheet GJM1555C1H1R0WB01-01A, specifications as of Apr. 15, 2026)",+ "href": "docs/gjm1555c1h1r0wb01d-datasheet.pdf"+ }+ ],+ "files": {+ "package-reference.step": "03e44c4b2b727b8d4b6b1fd598cbd12dd5e614f1dfeefa96a92a07e3a3b312a2",+ "package-reference.glb": "100935c5c0d6f20a6c9bdfb6386f874dc8a4457bf0e4f3ace69f1ab090100773",+ "docs/gjm1555c1h1r0wb01d-datasheet.pdf": "b3ecfddddfd268d8ef97b7c3a3085de92738a9a6bf995d9b1941f634663ea470"+ }+ },+ {+ "date": "2026-10-19",+ "by": {+ "name": "Claude (AI agent) for John Lauer",+ "handle": "john"+ },+ "status": "verified",+ "title": "Marking: the model shows the real part, which is unmarked",+ "scope": [+ "3d-model",+ "marking"+ ],+ "variants": [+ "*"+ ],+ "checks": [+ {+ "text": "The Murata reference sheet specifies no marking for this part: none of its 29 pages has a marking section or mentions a part marking, and the p.2 dimension sketch shows a plain body (GJM1555C1H1R0WB01-01A, all 29 pages searched; p.2)",+ "result": "pass"+ },+ {+ "text": "AI-added MPN etch removed (gjm1555c1h1r0wb01d-mpn.step, gjm1555c1h1r0wb01d-mpn.glb, docs/gjm1555c1h1r0wb01d-mpn.check.png)",+ "result": "pass"+ }+ ],+ "evidence": [+ {+ "label": "datasheet",+ "href": "docs/gjm1555c1h1r0wb01d-datasheet.pdf"+ }+ ],+ "files": {+ "package-reference.step": "03e44c4b2b727b8d4b6b1fd598cbd12dd5e614f1dfeefa96a92a07e3a3b312a2"+ }+ },+ {+ "date": "2026-10-19",+ "by": {+ "name": "Claude (AI agent) for John Lauer",+ "handle": "john"+ },+ "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": {+ "lod/high.glb": "68acbdcb91d8e05c66efc501487869191d7f8d9109a28a9783d28651d4b45dac",+ "lod/medium.glb": "4472dbe7df7a96447c93f320a683313eeee31981de733eaa9dd185ea0d0422a2",+ "lod/low.glb": "97c28a2b6729215f6ac551885121829bfecd90f647b98917dda123fd5dca1c29",+ "lod/super-low.glb": "f43ec4fa773604481f92061a583e06c0a643e4fd35ec0d9b2ebe1979f7ba50f9"+ }+ }+ ]+}