enginehall 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. enginehall-2.0.0.dist-info/METADATA +511 -0
  2. enginehall-2.0.0.dist-info/RECORD +75 -0
  3. enginehall-2.0.0.dist-info/WHEEL +4 -0
  4. enginehall-2.0.0.dist-info/entry_points.txt +4 -0
  5. enginehall-2.0.0.dist-info/licenses/LICENSE +51 -0
  6. pcba/THIRD_PARTY_NOTICES.md +72 -0
  7. pcba/__init__.py +6 -0
  8. pcba/adapters/__init__.py +10 -0
  9. pcba/adapters/altium_bridge/MANIFEST.json +5 -0
  10. pcba/adapters/altium_bridge/__init__.py +1 -0
  11. pcba/adapters/altium_bridge/bridge.pas +3342 -0
  12. pcba/adapters/altium_drive.py +1160 -0
  13. pcba/adapters/altium_house.py +591 -0
  14. pcba/adapters/altium_ops.py +858 -0
  15. pcba/adapters/altium_outjob.py +247 -0
  16. pcba/adapters/altium_uia.py +304 -0
  17. pcba/adapters/altium_vcs.py +197 -0
  18. pcba/adapters/altium_win32.py +457 -0
  19. pcba/adapters/cubemx_drive.py +248 -0
  20. pcba/baseline.py +151 -0
  21. pcba/changes.py +81 -0
  22. pcba/cli.py +891 -0
  23. pcba/components.py +323 -0
  24. pcba/config.py +180 -0
  25. pcba/data/components/generic.json +61 -0
  26. pcba/discover.py +350 -0
  27. pcba/doctor.py +131 -0
  28. pcba/house.py +123 -0
  29. pcba/mcp/__init__.py +5 -0
  30. pcba/mcp/altium_server.py +1358 -0
  31. pcba/mcp/protocol.py +224 -0
  32. pcba/mcp/server.py +332 -0
  33. pcba/model/__init__.py +38 -0
  34. pcba/model/design.py +232 -0
  35. pcba/model/finding.py +148 -0
  36. pcba/model/pinmap.py +200 -0
  37. pcba/parsers/__init__.py +0 -0
  38. pcba/parsers/altium/__init__.py +37 -0
  39. pcba/parsers/altium/_sheet.py +293 -0
  40. pcba/parsers/altium/geometry.py +103 -0
  41. pcba/parsers/altium/netlist.py +302 -0
  42. pcba/parsers/altium/pinmap.py +336 -0
  43. pcba/parsers/altium/project.py +249 -0
  44. pcba/parsers/altium/records.py +227 -0
  45. pcba/parsers/firmware/__init__.py +5 -0
  46. pcba/parsers/firmware/index.py +250 -0
  47. pcba/parsers/ioc/__init__.py +49 -0
  48. pcba/parsers/ioc/mcu_db.py +180 -0
  49. pcba/parsers/ioc/reader.py +182 -0
  50. pcba/parsers/ioc/validate.py +192 -0
  51. pcba/parsers/kicad/__init__.py +103 -0
  52. pcba/parsers/kicad/board.py +269 -0
  53. pcba/parsers/kicad/kicad_cli.py +190 -0
  54. pcba/parsers/kicad/pinmap.py +90 -0
  55. pcba/parsers/kicad/schematic.py +517 -0
  56. pcba/parsers/kicad/sexpr.py +136 -0
  57. pcba/publish.py +269 -0
  58. pcba/report/__init__.py +7 -0
  59. pcba/report/json_report.py +39 -0
  60. pcba/report/sarif.py +128 -0
  61. pcba/report/text.py +304 -0
  62. pcba/requirements.py +320 -0
  63. pcba/review.py +177 -0
  64. pcba/rules/__init__.py +312 -0
  65. pcba/rules/cross_domain.py +496 -0
  66. pcba/rules/kicad_board.py +775 -0
  67. pcba/rules/kicad_common.py +167 -0
  68. pcba/rules/kicad_components.py +603 -0
  69. pcba/rules/kicad_schematic.py +621 -0
  70. pcba/rules/locate.py +22 -0
  71. pcba/rules/schematic.py +123 -0
  72. pcba/rules/signals.py +149 -0
  73. pcba/rules/spice.py +59 -0
  74. pcba/rules/stm32.py +155 -0
  75. pcba/simulation.py +143 -0
@@ -0,0 +1,511 @@
1
+ Metadata-Version: 2.5
2
+ Name: enginehall
3
+ Version: 2.0.0
4
+ Summary: EngineHall: where hardware gets built, checked and released, together. The engine checks that schematic, CubeMX configuration and firmware agree.
5
+ Project-URL: Homepage, https://enginehall.com
6
+ Author: Newmatik GmbH
7
+ License: EngineHall — Proprietary Software License
8
+
9
+ Copyright (c) 2026 Newmatik GmbH. All rights reserved.
10
+
11
+ This software and its accompanying documentation, rule packs, schemas, skills,
12
+ and fixtures (the "Software") are the confidential and proprietary property of
13
+ Newmatik GmbH.
14
+
15
+ NO LICENSE IS GRANTED for this source repository except by a separate written
16
+ agreement signed by Newmatik GmbH. In the absence of such an agreement, you may
17
+ not use, copy, modify, merge, publish, distribute, sublicense, sell, reverse
18
+ engineer, or create derivative works of the Software, in whole or in part.
19
+
20
+ Copies of the `enginehall` package (formerly `pcba`) and VS Code extension distributed by
21
+ Newmatik through enginehall.com, PyPI, the Visual Studio Marketplace, or Open VSX are offered
22
+ under the end-user license at https://enginehall.com/license. That grant does
23
+ not apply to this source repository.
24
+
25
+ Unauthorized copying of this file or of this repository, via any medium, is
26
+ strictly prohibited.
27
+
28
+ NOTICES
29
+
30
+ 1. This repository is NOT open source. Do not add an OSI license file, and do
31
+ not copy code out of it into any open-source project.
32
+
33
+ 2. Related Newmatik projects carry different licenses and are NOT governed by
34
+ this file:
35
+ - gerbtrace .................. BUSL-1.1
36
+ - altium-schdoc-viewer ....... MIT (public)
37
+ - package-renderer ........... UNLICENSED / internal
38
+ Code must not be moved between those projects and this one without a
39
+ deliberate licensing decision. In particular, nothing proprietary from this
40
+ repository may be copied into altium-schdoc-viewer, which is public.
41
+
42
+ 3. This repository must never contain customer design data. Customer design
43
+ output is owned by the customer, not by Newmatik, and may not be
44
+ redistributed as part of this Software. See AGENTS.md.
45
+
46
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
47
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
48
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL NEWMATIK GMBH BE
49
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
50
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
51
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52
+
53
+ EngineHall reports findings to assist human engineering review. It is not a
54
+ certification tool and does not constitute electrical sign-off.
55
+
56
+ Newmatik GmbH
57
+ https://enginehall.com
58
+ License-File: LICENSE
59
+ Keywords: altium,cubemx,embedded,firmware,review,schematic,stm32
60
+ Classifier: Development Status :: 5 - Production/Stable
61
+ Classifier: Intended Audience :: Developers
62
+ Classifier: Intended Audience :: Manufacturing
63
+ Classifier: License :: Other/Proprietary License
64
+ Classifier: Programming Language :: Python :: 3.11
65
+ Classifier: Programming Language :: Python :: 3.12
66
+ Classifier: Programming Language :: Python :: 3.13
67
+ Classifier: Programming Language :: Python :: 3.14
68
+ Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
69
+ Requires-Python: >=3.11
70
+ Requires-Dist: olefile>=0.46
71
+ Provides-Extra: dev
72
+ Requires-Dist: build>=1.2; extra == 'dev'
73
+ Requires-Dist: jsonschema>=4.0; extra == 'dev'
74
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
75
+ Requires-Dist: pytest>=8.0; extra == 'dev'
76
+ Requires-Dist: ruff>=0.5; extra == 'dev'
77
+ Requires-Dist: twine>=6.0; extra == 'dev'
78
+ Description-Content-Type: text/markdown
79
+
80
+ # EngineHall
81
+
82
+ *Where hardware gets built, checked and released, together.* A Newmatik product, formerly PCBA Studio.
83
+
84
+ EngineHall is the shared workspace of a hardware development project on [enginehall.com](https://enginehall.com):
85
+ open points, meetings, decisions, documentation, design reviews and releases, for a team and its development
86
+ partners. Its engine, described below, runs on your machine. It installs as the `enginehall` package; the commands
87
+ are `enginehall` and `hall` (`pcba` still works for one major version and says it is deprecated).
88
+
89
+ EngineHall's engine finds contradictions between an Altium or KiCad schematic, an STM32CubeMX
90
+ configuration, and firmware. A board can route one pin, CubeMX can configure another, and the code
91
+ can still compile; EngineHall checks those boundaries before they become bench failures. On a KiCad
92
+ project it also verifies the schematic, the parts and the board: KiCad's own ERC and DRC, rails,
93
+ decoupling, pull-ups, supply ranges and derating against component data, connector pinouts, the
94
+ Newmatik house DFM values, and declared SPICE testbenches.
95
+
96
+ The Python engine is the product. It runs locally, uploads nothing, and produces evidence-backed
97
+ findings in text, Markdown, JSON, or SARIF. The VS Code extension is an optional UI for running the
98
+ engine, viewing `.SchDoc` files, and keeping a Markdown project tracker. The public site lives in
99
+ `site/` and is published at [enginehall.com](https://enginehall.com).
100
+
101
+ ## Quickstart
102
+
103
+ Requirements: Python 3.11–3.14. Developing this repo also needs Node: 20, 22, or 24 for the extension and the pre-commit hook,
104
+ and Node 24.18 or a later 24.x with pnpm for the site.
105
+ From this source checkout:
106
+
107
+ ```sh
108
+ python3 -m venv .venv
109
+ source .venv/bin/activate
110
+ python -m pip install -e .
111
+ ```
112
+
113
+ For a public install without a source checkout, use the checked wheel served by the site:
114
+
115
+ ```sh
116
+ python -m pip install https://enginehall.com/engine/enginehall-2.0.0-py3-none-any.whl
117
+ ```
118
+
119
+ The PyPI listing is pending. The install page will switch to `enginehall==2.0.0` after
120
+ that listing is verified.
121
+
122
+ Confirm what EngineHall can see, then run a review:
123
+
124
+ ```sh
125
+ enginehall inspect .
126
+ enginehall review .
127
+ ```
128
+
129
+ Try the synthetic reference board with **Try the reference board** at
130
+ [enginehall.com/review](https://enginehall.com/review). It intentionally reports six injected
131
+ errors. Exit status `0` means the selected
132
+ failure threshold was clean, `1` means findings met it, and `2` means a required check could not
133
+ run or the review could not run.
134
+ Always read `coverage.skipped`, the compared-signal count, and
135
+ `coverage.statistics.uncompared_signals`; zero findings only describes the checks and signal
136
+ pairs the review could evaluate. Each unpaired CubeMX placement now names its pin and reason.
137
+
138
+ ## Project setup
139
+
140
+ For a single target, discovery finds the directory with the most `.SchDoc` files, the first `.ioc`,
141
+ firmware roots containing `Core/Src`, and the MCU part. Use explicit flags if discovery is ambiguous:
142
+
143
+ ```sh
144
+ enginehall review . --schematic-dir hardware --ioc config/board.ioc --part YOUR_STM32_PART
145
+ ```
146
+
147
+ `--ioc` and `--schematic-dir` are read relative to the project root (here `.`), then the working
148
+ directory. A path that exists in neither stops the review with exit `2` instead of quietly
149
+ falling back to discovery. When discovery finds several firmware trees, it keeps the ones beside
150
+ the `.ioc` and says so in `project.notes`; name others with `firmware_roots` in
151
+ `pcba-studio.toml`.
152
+
153
+ The alternate-function rule reads STM32CubeMX's MCU database (`db/mcu`) from the usual install
154
+ locations. Set the `PCBA_CUBEMX_DB` environment variable to use another copy, for example the same
155
+ one on every machine; without a database the rule is listed in `coverage.skipped`.
156
+
157
+ If the sheets hold more than one instance of the MCU, the pin map is not extracted until you name
158
+ one; an unannotated designator such as `U?` never selects an instance:
159
+
160
+ ```sh
161
+ enginehall review . --schematic-component U2
162
+ ```
163
+
164
+ For several boards or firmware targets, add `pcba-studio.toml` at the project root:
165
+
166
+ ```toml
167
+ version = 1
168
+ default_target = "controller"
169
+
170
+ [targets.controller]
171
+ part = "YOUR_ORDERABLE_STM32_PART"
172
+ schematic_component = "U1"
173
+ ioc = "config/controller.ioc"
174
+ schematic_dir = "hardware/controller"
175
+ firmware_roots = ["firmware/controller"]
176
+
177
+ [targets.interface]
178
+ ioc = "config/interface.ioc"
179
+ schematic_dir = "hardware/interface"
180
+ firmware_roots = ["firmware/interface"]
181
+ ```
182
+
183
+ Paths must exist and remain inside the project. Select a non-default target with `--target`:
184
+
185
+ ```sh
186
+ enginehall inspect . --target interface
187
+ enginehall review . --target interface
188
+ ```
189
+
190
+ ### KiCad projects
191
+
192
+ `enginehall review` finds a `.kicad_pro` like it finds `.SchDoc` sheets; `--kicad-project` (or
193
+ `kicad_project` on a target) chooses one. `kicad-cli` and `ngspice` are optional: without them,
194
+ ERC, DRC, the netlist cross-check and the testbenches are listed as not checked. An optional
195
+ `pcba-requirements.toml` declares rail voltages, derating limits, single-sided assembly, edge
196
+ clearance, connector pinouts and SPICE testbenches. Every rule ends as passed, failed, info, not
197
+ checked (with the reason) or not applicable in `coverage.rules`. Details:
198
+ [`docs/kicad-review.md`](docs/kicad-review.md).
199
+
200
+ ```sh
201
+ enginehall doctor --no-ping # kicad-cli and ngspice found?
202
+ enginehall review boards/main --format markdown
203
+ enginehall kicad netlist boards/main # the nets the engine built from the wires
204
+ ```
205
+
206
+ ### Requiring coverage
207
+
208
+ "No findings" from a check that never ran is not a pass. Name the checks that must have usable
209
+ input; if one could not run, or compared no signal at all, the review exits `2`:
210
+
211
+ ```sh
212
+ enginehall review . --require-rule cross_domain.pin.mismatch --require-rule cross_domain.firmware.hardcoded_pin_drift
213
+ ```
214
+
215
+ A signal on several pins of one side is not compared first-wins; the report lists it as not compared.
216
+ Placements are compared by pad as well as by name, so a board whose nets carry functional names
217
+ (`RMII_TXD0`, `WIFI_SPI_SCK`) is still compared pin by pin.
218
+ The browser review accepts the same `pcba-studio.toml`, target, MCU overrides, baseline, and
219
+ required-rule inputs. Select a project folder to keep configured paths intact. Local Git
220
+ `--changed-only` and the installed CubeMX alternate-function database remain CLI-only.
221
+
222
+ ### Existing findings
223
+
224
+ Create an auditable baseline when adopting the checker in an established project:
225
+
226
+ ```sh
227
+ enginehall baseline . --reason "Reviewed during initial adoption"
228
+ enginehall review . --baseline .pcba-baseline.json
229
+ ```
230
+
231
+ The baseline stores stable finding IDs and a required reason for every suppression. Reviews report
232
+ both the number suppressed and stale IDs that no longer match a finding. Review and commit the file;
233
+ do not use it to hide new defects.
234
+
235
+ A `cross_domain.firmware.hardcoded_pin_drift` ID is derived from the `.ioc`, the pin, and the pin's
236
+ `.ioc` key, not from the first source file that names the pin, so adding another source file no
237
+ longer changes it. Baselines written by earlier versions suppress this rule under the old
238
+ file-based ID: regenerate the baseline once, or the old entry is reported as stale and the finding
239
+ reappears.
240
+
241
+ ### Changed files and pull requests
242
+
243
+ Limit local output to findings whose location or evidence touches a changed file:
244
+
245
+ ```sh
246
+ enginehall review . --changed-only origin/main
247
+ ```
248
+
249
+ The source repository is private, so public repositories install the checked wheel from
250
+ enginehall.com in CI, exactly as the [documentation](https://enginehall.com/docs#ci) shows. Switch
251
+ the install line to `enginehall==2.0.0` once the PyPI listing is published. Keep full Git history when
252
+ using `--changed-only`:
253
+
254
+ ```yaml
255
+ permissions:
256
+ contents: read
257
+
258
+ steps:
259
+ - uses: actions/checkout@v7
260
+ with:
261
+ fetch-depth: 0
262
+ - uses: actions/setup-python@v7
263
+ with:
264
+ python-version: "3.12"
265
+ - run: python -m pip install https://enginehall.com/engine/enginehall-2.0.0-py3-none-any.whl
266
+ - run: enginehall review . --require-rule cross_domain.pin.mismatch
267
+ ```
268
+
269
+ The composite action in this private repository remains available to Newmatik projects. It runs a
270
+ changed-only review, can upload SARIF, and preserves findings even when required coverage is
271
+ incomplete. Add the firmware rule to public CI when the repository has firmware.
272
+
273
+ ### Publishing to a development project
274
+
275
+ `--publish` sends the report (findings, their locations and evidence, and coverage) to the check
276
+ runs of a development project on the site. No design file or source file is sent, and local paths
277
+ are made project-relative. A lead creates the project token under Members, Tokens; pass it in an
278
+ environment variable, never on the command line:
279
+
280
+ ```sh
281
+ PCBA_WORKSPACE_TOKEN=... enginehall review . --publish --project DEMO --url https://enginehall.com
282
+ ```
283
+
284
+ The run carries its commit and repository, so a release drafted from it records the commit:
285
+ `--commit` defaults to `GITHUB_SHA`, else `git rev-parse HEAD` in the reviewed folder, and
286
+ `--repository` (a repository id, or its URL) defaults to the `origin` remote, sent as
287
+ `https://host/path` without credentials. The site matches the URL against the project's connected
288
+ repositories; one it does not know is noted, not an error, and a folder without git publishes
289
+ without either. `--token-env NAME` reads another variable. The exit codes
290
+ stay the same, except that a failed publish exits `2`. The action's `publish`, `project` and
291
+ `workspace-url` inputs do the same, with `PCBA_WORKSPACE_TOKEN` set from a secret in the step's
292
+ `env`.
293
+
294
+ ## What it checks
295
+
296
+ | Rule | Detects |
297
+ | --- | --- |
298
+ | `cross_domain.mcu.part_mismatch` | The schematic and CubeMX file identify different MCU parts; pin comparisons are skipped until the inputs match. |
299
+ | `cross_domain.pin.mismatch` | A peripheral signal routed to different pins in the schematic and `.ioc`. |
300
+ | `cross_domain.pin.signal_conflict` | A pad whose schematic net names a different peripheral than the `.ioc` assigns to it (SPI2_MOSI on a pin configured as I2C1_SDA). |
301
+ | `cross_domain.firmware.hardcoded_pin_drift` | Hand-written code that writes, configures, or otherwise names a pin now assigned to a peripheral in the `.ioc`. Reads (`HAL_GPIO_ReadPin`) do not count. |
302
+ | `cross_domain.pin.unconfigured` | A schematic peripheral signal absent from the `.ioc`. |
303
+ | `cross_domain.mcu_config.incomplete` | A placeholder or wrong `.ioc` covering almost none of the schematic. |
304
+ | `cross_domain.peripheral.unconnected` | A configured peripheral with no matching schematic connection. |
305
+ | `schematic.net.mislabelled` | A label whose pin prefix disagrees with geometric connectivity. |
306
+ | `schematic.net.open_circuit` | A part that nearly touches an MCU pin but stops short. This rule is heuristic. |
307
+ | `stm32.pin.alternate_function_impossible` | A peripheral signal the selected pin cannot carry according to ST's MCU database. |
308
+
309
+ On KiCad projects (see [`docs/kicad-review.md`](docs/kicad-review.md) for what each needs):
310
+
311
+ | Rule | Detects |
312
+ | --- | --- |
313
+ | `kicad.erc.violation` | A violation KiCad's ERC reports (kicad-cli). |
314
+ | `kicad.netlist.parity` | The engine's geometric connectivity disagrees with KiCad's netlist export (kicad-cli). |
315
+ | `kicad.power.rail_without_source` | A net with power inputs but no power output or PWR_FLAG. |
316
+ | `kicad.power.pin_without_dc_path` | An IC power pin that reaches no rail through a DC path (only through a capacitor, or not at all). |
317
+ | `kicad.power.decoupling_missing` | An IC supply net with no capacitor to ground. |
318
+ | `kicad.part.shorted` | A two-pin part with both pins on one net. |
319
+ | `kicad.pin.input_floating` | An input pin connected to nothing and not marked no-connect. |
320
+ | `kicad.net.pull_up_missing` | An I2C or open-drain net without a pull-up. |
321
+ | `kicad.label.global_single_sheet` | A global label used on one sheet only (info). |
322
+ | `kicad.label.single_connection` | A label whose net reaches at most one pin. |
323
+ | `kicad.net.name_hygiene` | `VBUS` without USB, a rail without a voltage in its name (info). |
324
+ | `kicad.component.supply_range` | A supply pin's rail outside the part's operating range or absolute maximum. |
325
+ | `kicad.component.capacitor_derating` | A capacitor above its derated voltage rating. |
326
+ | `kicad.component.resistor_power` | A resistor between two rails above its derated power rating. |
327
+ | `kicad.component.logic_level` | Driver levels a receiver does not recognise, or an input driven past its rating. |
328
+ | `kicad.component.required_connection` | A pin without the connection its component data requires (NRST capacitor, enable not floating, pull-ups). |
329
+ | `kicad.connector.pin_assignment` | A connector pin that does not carry the signal its interface spec names. |
330
+ | `kicad.drc.violation` | A violation or unconnected item KiCad's DRC reports (kicad-cli). |
331
+ | `kicad.drc.schematic_parity` | Board and schematic out of step (kicad-cli). |
332
+ | `kicad.dfm.fiducials` | House fiducials missing or wrong (NM-FID-001..006). |
333
+ | `kicad.dfm.track_angle` | A track not at 0, 45 or 90 degrees (NM-ROUTE-001). |
334
+ | `kicad.dfm.via_in_pad` | A via in or touching a pad (NM-VIA-001). |
335
+ | `kicad.dfm.via_spacing` | Two same-net vias closer than the house distance (NM-VIA-002). |
336
+ | `kicad.dfm.model_missing` | A footprint without a STEP model on disk (NM-3D-001). |
337
+ | `kicad.dfm.silkscreen_outside` | Silkscreen beyond the board outline (NM-SILK-001). |
338
+ | `kicad.dfm.barcode_field` | No or too small barcode field (NM-BC-001). |
339
+ | `kicad.dfm.bottom_side_smd` | An SMD part on the bottom of a board declared single-sided. |
340
+ | `kicad.dfm.smd_edge_clearance` | SMD copper closer to the edge than the declared clearance. |
341
+ | `kicad.dfm.test_point_coverage` | Nets without a test point. |
342
+ | `kicad.dfm.courtyard_overlap` | Overlapping courtyards. |
343
+ | `simulation.spice.testbench` | A declared ngspice testbench outside its pass range. |
344
+
345
+ Every finding has a project-relative location, quoted evidence, consequence, recommendation, rule
346
+ ID, severity, and detector classification. JSON output follows
347
+ the [public finding schema](https://enginehall.com/schemas/finding.schema.json).
348
+
349
+ ## Architecture
350
+
351
+ ```text
352
+ artifact discovery / target config
353
+ |
354
+ v
355
+ .SchDoc + .ioc + firmware -> normalized project model
356
+ | |
357
+ v v
358
+ parsers --------------------> rules
359
+ |
360
+ baseline / changed-file filter
361
+ |
362
+ v
363
+ text / Markdown / JSON / SARIF
364
+ ```
365
+
366
+ - `pcba/review.py` is the shared orchestration layer used by the CLI and engine MCP server.
367
+ - `pcba/parsers/` reads Altium OLE2 streams, CubeMX `.ioc` files, ST MCU metadata, and hand-written
368
+ firmware references.
369
+ - `pcba/rules/` compares the normalized model. Schematic connectivity comes from geometry; labels
370
+ only name nets.
371
+ - `pcba/report/` renders stable machine and human formats.
372
+ - `extension/` contains the optional VS Code UI, its heuristic viewer parser, the Markdown tracker,
373
+ and a separate single-artifact/tracker MCP server.
374
+
375
+ The engine parser is authoritative for review findings. The TypeScript parser serves interactive
376
+ viewing; both parsers run the same conformance corpora (record streams, placed-pin selection) to
377
+ prevent format drift.
378
+
379
+ ## Common commands
380
+
381
+ ```sh
382
+ enginehall inspect . # artifacts, selected target, and rule coverage
383
+ enginehall review . # all applicable checks
384
+ enginehall review . --format json # text, markdown, json, or sarif
385
+ enginehall review . --fail-on warning # choose the failing severity threshold
386
+ enginehall baseline . --reason "Reviewed" # write .pcba-baseline.json atomically
387
+ enginehall altium pinmap --dir hardware # geometric MCU pin-to-net map
388
+ enginehall kicad netlist boards/main # KiCad nets built from wire geometry
389
+ enginehall kicad pinmap boards/main # KiCad MCU pin-to-net map
390
+ enginehall altium project-sheets --project Synthetic.PrjPcb --format json # structural sheet inventory only
391
+ enginehall cubemx pins board.ioc # CubeMX pin assignments
392
+ enginehall cubemx af board.ioc PA2 # allowed alternate functions
393
+ enginehall cubemx diff old.ioc new.ioc # compare generated configurations
394
+ enginehall cubemx validate board.ioc manifest.json
395
+ enginehall mcp # engine MCP server over stdio
396
+ ```
397
+
398
+ Extension development is separate and optional:
399
+
400
+ ```sh
401
+ cd extension
402
+ npm ci
403
+ npm test
404
+ npm run package
405
+ ```
406
+
407
+ Open `extension/` in VS Code and press F5. The viewer and tracker work without the Python package;
408
+ the `EngineHall: Run Hardware/Firmware Review` command requires `pcba` on the extension host's
409
+ `PATH`. Findings appear in the Problems panel.
410
+
411
+ To register the engine with an MCP client, activate the virtualenv and use:
412
+
413
+ ```json
414
+ {
415
+ "mcpServers": {
416
+ "enginehall": { "command": "enginehall", "args": ["mcp"] }
417
+ }
418
+ }
419
+ ```
420
+
421
+ The local servers (`enginehall mcp`, `enginehall altium mcp`, and the extension's) review design files on your
422
+ computer. A hosted server at `https://enginehall.com/mcp` lets an assistant read your account and
423
+ the saved projects of your workspaces, and read and draft items in your development projects
424
+ (every write is previewed and needs your approval); it never receives design files:
425
+
426
+ ```sh
427
+ claude mcp add --transport http pcbastudio https://enginehall.com/mcp
428
+ ```
429
+
430
+ Setup for other clients, the tool list, and the safety model: <https://enginehall.com/docs/mcp>.
431
+
432
+ ### Claude Code plugin
433
+
434
+ The repository is also a Claude Code plugin marketplace (`.claude-plugin/`). For anyone with
435
+ access to the repository:
436
+
437
+ ```text
438
+ /plugin marketplace add newmatik/pcbastudio
439
+ /plugin install enginehall@newmatik
440
+ ```
441
+
442
+ The plugin brings the seven skills and three MCP servers: `pcba` (review), `pcba-altium` (Altium
443
+ drive, Windows with Altium running) and `fusion` (Autodesk Fusion's local server on port 27182, or
444
+ `FUSION_MCP_PORT`). The Python servers run with `uvx` from the plugin itself, so
445
+ [uv](https://docs.astral.sh/uv/) is the only requirement: no virtualenv, nothing on `PATH`. The
446
+ plugin has no version number, so every commit on `main` is an update
447
+ (`claude plugin update enginehall@newmatik`). Try a working tree with `claude --plugin-dir .`.
448
+ Claude Code hides a plugin server that duplicates one you registered yourself, so remove an
449
+ earlier `fusion` or `pcba` entry (`claude mcp remove fusion -s user`) to use the plugin's.
450
+ The extension's schematic and tracker server ships with the extension: run **EngineHall: Set up
451
+ MCP server for AI chat** in VS Code or Cursor.
452
+
453
+ ## Contributing
454
+
455
+ ### Codex and Claude Code skills
456
+
457
+ From this source checkout, install the six maintained skills for both clients:
458
+
459
+ ```sh
460
+ python3 tools/install-skills.py
461
+ # Or select just one client:
462
+ python3 tools/install-skills.py --clients codex
463
+ ```
464
+
465
+ The installer links into each client's user skill directory.
466
+ With the Claude Code plugin installed, link for Codex only (`--clients codex`); Claude Code
467
+ would otherwise list every skill twice.
468
+ It repairs links after moving the checkout and leaves real directories untouched. Skills stay
469
+ in this repository, so later edits are picked up without copying them again. On Windows,
470
+ creating directory symlinks requires Developer Mode or an elevated terminal.
471
+
472
+ Codex MCP configuration uses TOML; `.mcp.json` is the Claude Code configuration. Register
473
+ the review engine with an absolute virtualenv executable so GUI launches work:
474
+
475
+ ```sh
476
+ codex mcp add pcba -- /absolute/venv/bin/python -m pcba.cli mcp
477
+ codex mcp add enginehall-extension -- /absolute/node /absolute/extension/dist/mcp-server.js
478
+ codex mcp add fusion --url http://127.0.0.1:27182/mcp
479
+ ```
480
+
481
+ The extension bundle provides schematic viewing and tracker tools. Build it with
482
+ `cd extension && npm run build` first. The engine and extension have separate tool sets.
483
+ On Windows, register Altium's separate drive server with the virtualenv's Python:
484
+
485
+ ```powershell
486
+ codex mcp add pcba-altium -- C:\absolute\venv\Scripts\python.exe -m pcba.cli altium mcp
487
+ ```
488
+
489
+ Start a new client session after registering servers or skills. Altium editing requires the
490
+ MCP process to run in the logged-in Windows desktop session with Altium running. A Mac can
491
+ load its tool catalogue, but cannot drive Altium locally; an SSH service session also lacks
492
+ the interactive desktop. `enginehall doctor` reports the local prerequisites and connection fixes.
493
+
494
+ Read `AGENTS.md` in the source checkout before changing code. In particular:
495
+
496
+ - Never commit customer schematics, `.ioc` values, net labels, part numbers, pin maps, or project
497
+ names. Extend `fixtures/synthetic/` instead.
498
+ - Treat `.ioc` files as CubeMX-owned. Any write must use the adapter's inspect, plan, preview,
499
+ approve, apply, reinspect, verify, and record workflow.
500
+ - Add deterministic parsing and rules before explanation. Give every finding navigable evidence.
501
+ - Add a synthetic defect and exact golden assertion for a new rule. Run `tools/precommit.sh` before
502
+ every commit, and keep one logical change per English commit.
503
+
504
+ The site (`site/`) runs locally on port 3090, its Worker preview on 8790, and its local Supabase
505
+ stack on 60321–60324; `AGENTS.md` lists the commands.
506
+
507
+ The supported product path is STM32 firmware with Altium or KiCad schematics. EngineHall assists review; it
508
+ does not provide certification or engineering sign-off. See `docs/roadmap.md` in the source for
509
+ scope and non-goals.
510
+
511
+ Proprietary software, © 2026 Newmatik GmbH. See the [license](https://enginehall.com/license).
@@ -0,0 +1,75 @@
1
+ pcba/__init__.py,sha256=TYO0wdbrB4UEge7s2Mlji4tqPI7ttrZ7MRDSv3LAZDU,206
2
+ pcba/baseline.py,sha256=rJ2hZKKUmIJJuVHMjbnXsoZKxj1TufUEyrkP0zYJxOU,5329
3
+ pcba/changes.py,sha256=EA9awXnt6koUTtp7o9IOSQeMpVwDbkbly0EW0eHSO2k,2905
4
+ pcba/cli.py,sha256=WMZAzu31_0RylVV3AC9qzGAx1Slgv7WV7OuJ_7qb6o8,40691
5
+ pcba/components.py,sha256=0SZ3J_ryBx34i9FW3yiJs1LnOVnGXtdFKnc2HSFtrlQ,14139
6
+ pcba/config.py,sha256=Cj4V9h_ZI0TZs9jQAhlN7AqIj5TzJvkrWiLDeUjMmEY,7015
7
+ pcba/discover.py,sha256=0zAbNggW7stLeXBEjnFtp5JRaRy61p4ALofP4UGqNao,15977
8
+ pcba/doctor.py,sha256=gN_ooRx5ZPm7djRFolXxQq67r2KEvjg6NaROLmuIMIU,6733
9
+ pcba/house.py,sha256=4P1WTRqXkn_JwqDVYB1h1KaRER7TsKvGUKdIsMvgkD0,5180
10
+ pcba/publish.py,sha256=xZxUDs2GnxPeIAk3uC7rcv-xSPEDJQoikajSfPlvv98,10732
11
+ pcba/requirements.py,sha256=76-m9-fKFCpwCvL_tnGtlRf7LSsxMLkJF49tzrV4cp4,13434
12
+ pcba/review.py,sha256=AUQORoOB5ZvA6prVp4FBZcQ0ygzfV_39kTyzv42AK3w,7786
13
+ pcba/simulation.py,sha256=o0hj7Xq25LozSQonuCBSiqw9h_0vXkxr8EoWloREj2A,7277
14
+ pcba/adapters/__init__.py,sha256=prKHiczm5sezA1JjMMoDLAs9nBORiuTRW5hdDtvuKSM,529
15
+ pcba/adapters/altium_drive.py,sha256=v7cLYP1VrJ9DJ053cgzBSiIJZNd3r-JmBahp55B60w0,55508
16
+ pcba/adapters/altium_house.py,sha256=rWMp-0kqi1yOu8YEr8KfJUPx7W9n03--ZplpPn22fHY,31750
17
+ pcba/adapters/altium_ops.py,sha256=VLc60EBMPIWJroXRW2tgWoXqJ1jT3GOrIfbJ-ux_-_0,45809
18
+ pcba/adapters/altium_outjob.py,sha256=zTmrHYJ-mNcuUT4CZqcUSa1DLQyqMPBQjUjSHwS8Vkc,16896
19
+ pcba/adapters/altium_uia.py,sha256=RtfN4pxGffkUGW-F0KkEsGGT2TZ1GLIbNDNbIaMQHXg,12567
20
+ pcba/adapters/altium_vcs.py,sha256=3PY1TwIFXnaOEXsUCnCt5zW97sMSaziwXkWhaDBxhKY,8783
21
+ pcba/adapters/altium_win32.py,sha256=vlIpb61_5Ld4RRhPRfmQfxo0jLrTfNiqwNOb6DKK1Ak,17572
22
+ pcba/adapters/cubemx_drive.py,sha256=JRkMRHcvHlg-rU59J_71-kuIXR73wHVfokaXaBUKUCQ,9793
23
+ pcba/adapters/altium_bridge/MANIFEST.json,sha256=VWUtYKu_ZqmGtYvogEy1qvUVjZXRyhNLB1ZSBX5oPQE,129
24
+ pcba/adapters/altium_bridge/__init__.py,sha256=8hk5LYHrzRB7C4Oivzq9b4a8I64WYCT7AAIM0VMI8Y4,91
25
+ pcba/adapters/altium_bridge/bridge.pas,sha256=Xk9tZSwmY1vdbgt1fmW9c9gzIiDrGxd1t9EDyW8lGfQ,119191
26
+ pcba/data/components/generic.json,sha256=_J2oCDcX7JTTgY7CehuwUCLtQcIJDf3APlGnQ164Shs,3104
27
+ pcba/mcp/__init__.py,sha256=ipvX_bXMMm48zC5TvboKVjbGjg3h90uNOoTYBZsRcIA,138
28
+ pcba/mcp/altium_server.py,sha256=yUUvH5Q3uEdRFSCqONCNs6XSPl2FWmxGDz82CvQ74R0,73762
29
+ pcba/mcp/protocol.py,sha256=k1oygrVdlXSUTbLbu6hxnMXaYrvUzBOi2ZxtdNozAOk,9101
30
+ pcba/mcp/server.py,sha256=RvE5yyh7Z32gTnka7JnQcd4Gt5jVwRzp_B0wqh5OJ6U,13555
31
+ pcba/model/__init__.py,sha256=3HoN_YxC7UBpKeCAHAa4veKqGjAhWwPHJ4UwJRylB4k,717
32
+ pcba/model/design.py,sha256=BMzZjTThUYzk6K79WtuC-5K6863ZN19nBB4N2vsv_1c,8469
33
+ pcba/model/finding.py,sha256=DdgPqL9mSjx2SoTt1nM0UQiLLhBlmLDCAnOYtUYeLWU,5358
34
+ pcba/model/pinmap.py,sha256=lARQyxoIoQT1pKIM71oKTT3H7Y37gFPCLsvMw18bHcY,7841
35
+ pcba/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
+ pcba/parsers/altium/__init__.py,sha256=BwtrQDOyuBryXnTlr9zM0HX6EdP5wnGPtpWRFQ3YUYk,1102
37
+ pcba/parsers/altium/_sheet.py,sha256=V5Bphrpw4RgZKUl_2pcvrfh_5HmoP-zp-X3qMpi35YM,12216
38
+ pcba/parsers/altium/geometry.py,sha256=St70y6x7XZ0xqL6-OxtQjfoXp847yva_4QJyfy0s4u8,4122
39
+ pcba/parsers/altium/netlist.py,sha256=VrO9xobi03EcfXNDxaK4NOAT3uWTA3DKr3BAKYopmlo,12761
40
+ pcba/parsers/altium/pinmap.py,sha256=WyOx6wAEwD_J8t1PbClxM5FMruWe0A5zGFtYJ6sx5Kk,13963
41
+ pcba/parsers/altium/project.py,sha256=Y5dP48njN8ePJLecN0n5Ul-zdB4MYyUiqssRY85nDy8,10402
42
+ pcba/parsers/altium/records.py,sha256=hUIK9vhJiHGnm1wCJ4N_AtXUEEh7wNoKsJGKshNkkTc,9259
43
+ pcba/parsers/firmware/__init__.py,sha256=0gRow0u-u5y6ejqRJ-BmrlRLeCKSNNnyTQq13OiaNdM,124
44
+ pcba/parsers/firmware/index.py,sha256=Nit-gjRT0W7SARVCTnhh0LqVd_15l1jCLlHCGTIGJos,10628
45
+ pcba/parsers/ioc/__init__.py,sha256=1mhKH-hoOhvzEcEov0geycUw7G5SIGtlFueploUw5WI,1016
46
+ pcba/parsers/ioc/mcu_db.py,sha256=NoY5fy-3YYapYNNHlY3rOMrKm-3cDBkiRq6NZYtN8Iw,6667
47
+ pcba/parsers/ioc/reader.py,sha256=0diPODz-ZH8KeMwSoi3MrpQMh6gkDplQvepo5XiWC2c,6748
48
+ pcba/parsers/ioc/validate.py,sha256=QKubChjBf_vXzbD7srxz2VPTOPZ5TciWeAux8vFvESs,8414
49
+ pcba/parsers/kicad/__init__.py,sha256=DdREQ2NsWXISNg9VqTgZEiV8HkS_mMvqPK1NNpS8AHI,4753
50
+ pcba/parsers/kicad/board.py,sha256=gsDS7J0thYmJ1bmoDdkR4cEhqX8CxhCfJM9Ydqo7qK8,12046
51
+ pcba/parsers/kicad/kicad_cli.py,sha256=jia4Be97a2GiWHvEE8MtiL6O1HNFEoijqvSGSFPHSP0,8874
52
+ pcba/parsers/kicad/pinmap.py,sha256=mYvPpo8IAGrqILxvnyohrM7dGnIPluN5HOldE__ULmI,4285
53
+ pcba/parsers/kicad/schematic.py,sha256=Ir7zXTilH4l7w2fZC4fMFPhiv7abFI6-vWmQXbiikT8,24872
54
+ pcba/parsers/kicad/sexpr.py,sha256=Jf0miZAF7hl6wfQrPhFDy5C0wEnnc3LvsEKyH5HB6UA,4969
55
+ pcba/report/__init__.py,sha256=5EZshh3aR0zAMd0tQ8FLJz0qaEj9KG9IVTv4ENqJqYk,269
56
+ pcba/report/json_report.py,sha256=lxGhUkN_NWjsixj-yJcGh0XC3r4R6-8Fk46koOtN9y0,1225
57
+ pcba/report/sarif.py,sha256=KyYZ2pltsh_1sz-WBedQYMYUyXP9fOB1I8hKC_MAayI,4668
58
+ pcba/report/text.py,sha256=RKp6vFUUcLFxEB8z_DjVJUkWdw97tW5O0vYJm9GLxxI,12136
59
+ pcba/rules/__init__.py,sha256=Je0Sl6nJceFWHTbJMJJ9vmRNOY6ZgeGcuntEpOCwIqY,15969
60
+ pcba/rules/cross_domain.py,sha256=qj2dW4dI3nFyGG4_6bxnq-c7CJ3ZupQvMoBy0d1Q15g,22377
61
+ pcba/rules/kicad_board.py,sha256=XO3ipqc_kVahvD_CaBeCiT4hKjQOhLAwt8qViHFaaJQ,38341
62
+ pcba/rules/kicad_common.py,sha256=iq8-LLRCB7tGr6UJP-D319oEA9ivAr61cUSDkdIDU3M,6804
63
+ pcba/rules/kicad_components.py,sha256=REjin3PL8KHlJjHgX7j8epRBsnVv-wVrBMNZXTtgzKE,29552
64
+ pcba/rules/kicad_schematic.py,sha256=DpN8RuUCpmpLTYeFIioEB1pQHHbBiz47QxTvOJ903Cc,31361
65
+ pcba/rules/locate.py,sha256=6mjr-fQLKSzMmgjDfD9krfPWtz5BwleNvKa6fWwOatQ,627
66
+ pcba/rules/schematic.py,sha256=r96tB0qdQjbR67ean9Tcd3zgWi39QldlTpQJFmFEh3E,5109
67
+ pcba/rules/signals.py,sha256=DS_lxImYH64iaKz8lZLY4BLMRhDquB_qtSO8YzbHFJ0,5986
68
+ pcba/rules/spice.py,sha256=JgsQODTvZYBttHpLvKErGLkuqML2vj9yM2JbpQqroBs,3045
69
+ pcba/rules/stm32.py,sha256=EituBzIP4m3xKqY5hpGfNAWunD4Nz22u2VfvGXZN8LA,6980
70
+ pcba/THIRD_PARTY_NOTICES.md,sha256=h5YbTUTGGJ4kVU01tX1JyMKVliGQ_nzfQ2onNTIIDqk,3334
71
+ enginehall-2.0.0.dist-info/METADATA,sha256=vteQ0Gzok4TuVk_JhacG5IjJVoV8BzA8-dAgMkBdysI,25788
72
+ enginehall-2.0.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
73
+ enginehall-2.0.0.dist-info/entry_points.txt,sha256=wngi4yUw4__bT6MknK7pJMnSPiaOexIO3DPXYwUNk_A,95
74
+ enginehall-2.0.0.dist-info/licenses/LICENSE,sha256=Q8oxDSxltMtcwM1_Ur1hq9z5l4II0ZavzB-DxoGtJ5I,2421
75
+ enginehall-2.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ enginehall = pcba.cli:run
3
+ hall = pcba.cli:run
4
+ pcba = pcba.cli:run_deprecated
@@ -0,0 +1,51 @@
1
+ EngineHall — Proprietary Software License
2
+
3
+ Copyright (c) 2026 Newmatik GmbH. All rights reserved.
4
+
5
+ This software and its accompanying documentation, rule packs, schemas, skills,
6
+ and fixtures (the "Software") are the confidential and proprietary property of
7
+ Newmatik GmbH.
8
+
9
+ NO LICENSE IS GRANTED for this source repository except by a separate written
10
+ agreement signed by Newmatik GmbH. In the absence of such an agreement, you may
11
+ not use, copy, modify, merge, publish, distribute, sublicense, sell, reverse
12
+ engineer, or create derivative works of the Software, in whole or in part.
13
+
14
+ Copies of the `enginehall` package (formerly `pcba`) and VS Code extension distributed by
15
+ Newmatik through enginehall.com, PyPI, the Visual Studio Marketplace, or Open VSX are offered
16
+ under the end-user license at https://enginehall.com/license. That grant does
17
+ not apply to this source repository.
18
+
19
+ Unauthorized copying of this file or of this repository, via any medium, is
20
+ strictly prohibited.
21
+
22
+ NOTICES
23
+
24
+ 1. This repository is NOT open source. Do not add an OSI license file, and do
25
+ not copy code out of it into any open-source project.
26
+
27
+ 2. Related Newmatik projects carry different licenses and are NOT governed by
28
+ this file:
29
+ - gerbtrace .................. BUSL-1.1
30
+ - altium-schdoc-viewer ....... MIT (public)
31
+ - package-renderer ........... UNLICENSED / internal
32
+ Code must not be moved between those projects and this one without a
33
+ deliberate licensing decision. In particular, nothing proprietary from this
34
+ repository may be copied into altium-schdoc-viewer, which is public.
35
+
36
+ 3. This repository must never contain customer design data. Customer design
37
+ output is owned by the customer, not by Newmatik, and may not be
38
+ redistributed as part of this Software. See AGENTS.md.
39
+
40
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
42
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL NEWMATIK GMBH BE
43
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
44
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
45
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
46
+
47
+ EngineHall reports findings to assist human engineering review. It is not a
48
+ certification tool and does not constitute electrical sign-off.
49
+
50
+ Newmatik GmbH
51
+ https://enginehall.com