spiceguard 0.1.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Moiz Lakkadkutta
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,341 @@
1
+ Metadata-Version: 2.4
2
+ Name: spiceguard
3
+ Version: 0.1.0
4
+ Summary: Can I trust this SPICE result? Catches silent/wrong ngspice simulation results.
5
+ Author-email: Moiz Lakkadkutta <moizlakkadkutta1@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Moiz Lakkadkutta
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/moiz-lakkadkutta/circuit
29
+ Project-URL: Repository, https://github.com/moiz-lakkadkutta/circuit
30
+ Project-URL: Issues, https://github.com/moiz-lakkadkutta/circuit/issues
31
+ Keywords: spice,ngspice,circuit,simulation,eda,electronics,kicad,ltspice,netlist,verification,convergence
32
+ Classifier: Development Status :: 4 - Beta
33
+ Classifier: Environment :: Console
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: Intended Audience :: Science/Research
36
+ Classifier: License :: OSI Approved :: MIT License
37
+ Classifier: Operating System :: OS Independent
38
+ Classifier: Programming Language :: Python :: 3
39
+ Classifier: Programming Language :: Python :: 3.9
40
+ Classifier: Programming Language :: Python :: 3.10
41
+ Classifier: Programming Language :: Python :: 3.11
42
+ Classifier: Programming Language :: Python :: 3.12
43
+ Classifier: Programming Language :: Python :: 3.13
44
+ Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
45
+ Classifier: Topic :: Utilities
46
+ Requires-Python: >=3.9
47
+ Description-Content-Type: text/markdown
48
+ License-File: LICENSE
49
+ Dynamic: license-file
50
+
51
+ # circuit / spiceguard
52
+
53
+ This repo started as **CircuitCLI**, an image/photo-to-SPICE-simulation pipeline
54
+ (YOLO + OCR + graph → ngspice). That idea was dropped after research showed the
55
+ problem it targeted ("redrawing schematics") isn't a pain users actually report,
56
+ and the obvious adjacent gaps were already taken or funded.
57
+
58
+ It is now exploring a different, evidence-led direction: a **SPICE result
59
+ trust-guard**.
60
+
61
+ ---
62
+
63
+ ## What is spiceguard?
64
+
65
+ Modern ngspice recovers from many classic convergence problems on its own — and
66
+ when it can't, it often returns **exit code 0 with a plausible but wrong answer**
67
+ (a relaxed fallback estimate, or arbitrary voltages on an ungrounded node).
68
+ Nothing in the standard flow warns you.
69
+
70
+ `spiceguard` answers one question about a SPICE run: **can I trust this result?**
71
+
72
+ It combines static netlist analysis, ngspice failure-log decoding (cryptic
73
+ internal names translated to the real component plus a specific fix), and
74
+ silent-failure detection (exit-0 runs that are still untrustworthy). It accepts
75
+ netlists from ngspice, KiCad, LTspice, and PSpice, and can convert LTspice `.asc`
76
+ schematics (experimental, built-in 2-pin symbols only).
77
+
78
+ ---
79
+
80
+ ## Install
81
+
82
+ **Requirements:** Python 3.9+, ngspice
83
+
84
+ ```bash
85
+ brew install ngspice # macOS; Linux: apt install ngspice
86
+ ```
87
+
88
+ **Install from the repo:**
89
+
90
+ ```bash
91
+ pip install .
92
+ ```
93
+
94
+ This registers a `spiceguard` command on your PATH. Alternatively, run directly
95
+ without installing (useful during development):
96
+
97
+ ```bash
98
+ PYTHONPATH=src python3 -m spiceguard FILE...
99
+ ```
100
+
101
+ ---
102
+
103
+ ## ngspice path resolution (priority order)
104
+
105
+ spiceguard locates the ngspice binary in this order:
106
+
107
+ 1. `--ngspice PATH` CLI flag — if given and not executable, raises an error immediately (no fallthrough)
108
+ 2. `$NGSPICE` environment variable — same hard-configured semantics; error if set but not usable
109
+ 3. `which ngspice` (PATH lookup)
110
+ 4. Legacy fallback `/opt/homebrew/bin/ngspice`
111
+
112
+ If none of the above resolves to a usable binary, spiceguard exits with code **3**
113
+ and prints a clear message listing what was tried. Install ngspice, or point to it
114
+ explicitly:
115
+
116
+ ```bash
117
+ spiceguard --ngspice /usr/local/bin/ngspice mynetlist.cir
118
+ # or
119
+ export NGSPICE=/usr/local/bin/ngspice
120
+ ```
121
+
122
+ ---
123
+
124
+ ## CLI usage
125
+
126
+ ```
127
+ spiceguard [--ngspice PATH] [--version] [--help] FILE...
128
+ spiceguard kicad [--ngspice PATH] FILE...
129
+ ```
130
+
131
+ Pass one or more netlist (or schematic) files. When multiple files are given,
132
+ spiceguard evaluates each in sequence and exits with the worst verdict across all.
133
+
134
+ ### Options
135
+
136
+ | Flag | Description |
137
+ |------|-------------|
138
+ | `FILE...` | One or more netlist or schematic files to check |
139
+ | `--ngspice PATH` | Explicit path to the ngspice binary |
140
+ | `--version` | Print version and exit |
141
+ | `--help` | Show usage |
142
+
143
+ ### Exit codes
144
+
145
+ | Code | Meaning |
146
+ |------|---------|
147
+ | 0 | TRUSTWORTHY — ngspice exited 0 and no trust issues found |
148
+ | 1 | FAILED — ngspice exited non-zero |
149
+ | 2 | SUSPECT — ngspice exited 0 but trust issues were detected |
150
+ | 3 | ngspice not found |
151
+ | 64 | Usage error (bad arguments) |
152
+
153
+ ### Example
154
+
155
+ ```
156
+ $ PYTHONPATH=src python3 -m spiceguard tests/netlists/n5_healthy_control.cir
157
+
158
+ ======================================================================
159
+ n5_healthy_control.cir
160
+ ======================================================================
161
+ ✓ TRUSTWORTHY (ngspice exit 0)
162
+
163
+ No trust issues detected.
164
+ ```
165
+
166
+ ```
167
+ $ PYTHONPATH=src python3 -m spiceguard tests/netlists/n1_missing_ground.cir
168
+
169
+ ======================================================================
170
+ n1_missing_ground.cir
171
+ ======================================================================
172
+ ⚠ SUSPECT (ngspice exit 0)
173
+
174
+ [FATAL] no_ground
175
+ → No node '0' (ground). SPICE has no voltage reference, so it may float the circuit and return arbitrary WRONG voltages with no error. Fix: tie a reference node to '0'.
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Input formats
181
+
182
+ | Extension | Handling |
183
+ |-----------|----------|
184
+ | `.cir`, `.net`, `.sp`, `.spice`, `.ckt` | Passed directly to ngspice |
185
+ | Any other netlist | ngspice natively translates KiCad, LTspice, PSpice, HSpice dialects |
186
+ | `.asc` | Converted in-process (experimental — see below) |
187
+
188
+ **LTspice `.asc` (experimental):** spiceguard converts `.asc` schematics using
189
+ built-in 2-pin symbol geometry (resistor, capacitor, inductor, diode, voltage
190
+ source, current source). Net connectivity is recovered by union-find over
191
+ wire/pin/flag coordinates. For anything beyond these built-in symbols, export
192
+ the netlist from LTspice ("View > SPICE Netlist") and feed that instead — that
193
+ path is exact. When a `.asc` is evaluated, the generated netlist is printed at
194
+ the end of the report so you can compare it against LTspice's own export.
195
+
196
+ ### Subcircuit support (Feature B)
197
+
198
+ spiceguard's parser handles:
199
+
200
+ - `.subckt` / `.ends` block collection and extraction
201
+ - `X`-instance flattening with automatic node namespacing (internal nodes become
202
+ `instancename:node` to avoid collisions)
203
+ - `+` continuation lines rejoined before parsing
204
+ - `.include` file resolution (local files only; URL `.include` lines are warned
205
+ and skipped)
206
+
207
+ Errors detected during subcircuit processing:
208
+
209
+ - `undefined_subckt` — X-instance references a subckt name not defined in the netlist
210
+ - `port_mismatch` — X-instance provides a different number of nodes than the subckt port list
211
+ - `subckt_recursion` — self- or mutual-referencing subckts (skipped with a warning)
212
+
213
+ ---
214
+
215
+ ## KiCad workflow (Feature C)
216
+
217
+ ```bash
218
+ spiceguard kicad myboard.cir
219
+ ```
220
+
221
+ The `kicad` subcommand runs the standard trust check **plus** a KiCad-specific
222
+ preflight that detects the classic export gotcha:
223
+
224
+ **`kicad_ground_not_zero`** — ngspice requires the circuit ground to be exactly
225
+ node `0`. KiCad schematics commonly use a `GND` net (or `GNDA`, `VSS`, `0V`,
226
+ `AGND`, `DGND`, `PGND`) that is NOT automatically mapped to node 0 on SPICE
227
+ export. When such a net is present and node 0 is absent, the simulation silently
228
+ floats the entire circuit, yielding wrong voltages with no error. The message
229
+ includes KiCad-specific fix instructions (set node mapping in Symbol Properties
230
+ or place a `PWR_FLAG`).
231
+
232
+ **Pipe form** — export and check in a single step without writing a file:
233
+
234
+ ```bash
235
+ kicad-cli sch export netlist --format spice myboard.kicad_sch -o - \
236
+ | spiceguard kicad -
237
+ ```
238
+
239
+ **Important:** spiceguard is a command-line workflow helper, not a native KiCad
240
+ plugin. KiCad has no post-simulation event API, so there is no in-GUI integration;
241
+ run `spiceguard kicad` from your terminal or CI pipeline.
242
+
243
+ ---
244
+
245
+ ## Verdict model
246
+
247
+ Every run produces one of three verdicts:
248
+
249
+ | Verdict | Meaning |
250
+ |---------|---------|
251
+ | **TRUSTWORTHY** | ngspice exited 0 and no trust-breaking issues found |
252
+ | **SUSPECT** | ngspice exited 0 but at least one FATAL/SILENT/WARN issue detected |
253
+ | **FAILED** | ngspice exited non-zero |
254
+
255
+ FAILED outranks SUSPECT, which outranks TRUSTWORTHY (this is why exit code 1 < 2
256
+ numerically but FAILED is the worst outcome).
257
+
258
+ ### Detectors
259
+
260
+ | Code | Severity | Description |
261
+ |------|----------|-------------|
262
+ | `no_ground` | FATAL | No node '0' — circuit has no voltage reference |
263
+ | `source_conflict` | FATAL | Multiple voltage sources forced across the same node pair |
264
+ | `no_dc_path` | FATAL | Node reachable only through capacitors/current sources — no DC reference |
265
+ | `timestep_collapse` | FATAL | Transient timestep collapsed; specific culprit identified from the log |
266
+ | `singular_node` | FATAL | Singular matrix at a node — no defined DC solution |
267
+ | `singular_branch` | FATAL | Singular matrix at a branch (current unconstrained) |
268
+ | `silent_fallback` | SILENT | ngspice exited 0 after gmin/source stepping **failed**; operating point is a relaxed estimate, not a true solution |
269
+ | `kicad_ground_not_zero` | WARN | KiCad export uses GND/GNDA/etc. but no node 0 (kicad subcommand only) |
270
+ | `dangling_node` | WARN | Node connects to only one pin — likely a wiring mistake |
271
+
272
+ ---
273
+
274
+ ## Security
275
+
276
+ spiceguard runs ngspice in batch mode on the netlists you give it. **Treat a
277
+ netlist like a script you are about to run**, because in two ways it is one:
278
+
279
+ - A SPICE netlist can contain `.control`/`shell` directives that execute
280
+ arbitrary shell commands. The captured log may include their output.
281
+ - `.include` reads whatever file path the netlist specifies (the same files
282
+ ngspice itself would read), so a hostile netlist can point at files on your
283
+ disk. The contents are parsed as a netlist, not printed.
284
+
285
+ **Only run spiceguard on netlists you trust.** It is a local dev/CI utility,
286
+ not a sandbox.
287
+
288
+ Hardening that *is* in place:
289
+
290
+ - ngspice is invoked with `--no-spiceinit`, so a `.spiceinit`/`spice.rc` config
291
+ planted next to the netlist is **not** auto-executed.
292
+ - The simulator is launched with a fixed argument list, **never** through a
293
+ shell (no `shell=True`), so the path can't be shell-injected.
294
+ - A 120s timeout terminates a hung/non-converging run (reported as FAILED, not
295
+ a crash); temp files use `mkstemp` (0600) and are always cleaned up;
296
+ subcircuit nesting is depth-bounded against stack exhaustion.
297
+ - spiceguard has **no third-party runtime dependencies**, and the source passes
298
+ `bandit -r src/` with no findings (`pip-audit` reports no vulnerable deps).
299
+
300
+ ---
301
+
302
+ ## Limitations (honest scope)
303
+
304
+ - **Parser scope:** The static netlist parser covers the element types listed in
305
+ `netlist.py` (`NODE_COUNT`). Exotic or simulator-specific elements not in that
306
+ table are silently skipped (they do not affect the running netlist — ngspice
307
+ still sees the full file).
308
+ - **`.asc` is experimental** and scoped to 6 built-in 2-pin symbol types. Any
309
+ schematic with transistors, op-amps, subcircuit blocks, or custom symbols must
310
+ be exported from LTspice first. Always verify the generated netlist against
311
+ LTspice's own "View > SPICE Netlist".
312
+ - **Legacy fallback** (`/opt/homebrew/bin/ngspice`) is tier-4 last-resort only.
313
+ It is not the preferred path; prefer `PATH` or the `$NGSPICE` variable.
314
+ - **KiCad integration** is a CLI helper, not an in-application plugin.
315
+
316
+ ---
317
+
318
+ ## Development
319
+
320
+ Run the test suite (no ngspice required for unit tests; integration tests
321
+ auto-skip when ngspice is absent):
322
+
323
+ ```bash
324
+ PYTHONPATH=src python3 -m pytest -q
325
+ ```
326
+
327
+ Observed output on the current suite:
328
+
329
+ ```
330
+ 82 passed in 3.93s
331
+ ```
332
+
333
+ ---
334
+
335
+ ## How we got here
336
+
337
+ 1. Audited the original image-to-sim idea (market / engineering / business).
338
+ 2. Deep research across the full EDA/sim workflow + a competition cross-check.
339
+ 3. Ruled out taken/funded gaps (AR debugging = Cadence inspectAR; AI autorouting
340
+ = Quilter et al.; SI/PI = heavy field-solver work).
341
+ 4. Landed on the SPICE result-trustworthiness wedge and built the tool.
@@ -0,0 +1,291 @@
1
+ # circuit / spiceguard
2
+
3
+ This repo started as **CircuitCLI**, an image/photo-to-SPICE-simulation pipeline
4
+ (YOLO + OCR + graph → ngspice). That idea was dropped after research showed the
5
+ problem it targeted ("redrawing schematics") isn't a pain users actually report,
6
+ and the obvious adjacent gaps were already taken or funded.
7
+
8
+ It is now exploring a different, evidence-led direction: a **SPICE result
9
+ trust-guard**.
10
+
11
+ ---
12
+
13
+ ## What is spiceguard?
14
+
15
+ Modern ngspice recovers from many classic convergence problems on its own — and
16
+ when it can't, it often returns **exit code 0 with a plausible but wrong answer**
17
+ (a relaxed fallback estimate, or arbitrary voltages on an ungrounded node).
18
+ Nothing in the standard flow warns you.
19
+
20
+ `spiceguard` answers one question about a SPICE run: **can I trust this result?**
21
+
22
+ It combines static netlist analysis, ngspice failure-log decoding (cryptic
23
+ internal names translated to the real component plus a specific fix), and
24
+ silent-failure detection (exit-0 runs that are still untrustworthy). It accepts
25
+ netlists from ngspice, KiCad, LTspice, and PSpice, and can convert LTspice `.asc`
26
+ schematics (experimental, built-in 2-pin symbols only).
27
+
28
+ ---
29
+
30
+ ## Install
31
+
32
+ **Requirements:** Python 3.9+, ngspice
33
+
34
+ ```bash
35
+ brew install ngspice # macOS; Linux: apt install ngspice
36
+ ```
37
+
38
+ **Install from the repo:**
39
+
40
+ ```bash
41
+ pip install .
42
+ ```
43
+
44
+ This registers a `spiceguard` command on your PATH. Alternatively, run directly
45
+ without installing (useful during development):
46
+
47
+ ```bash
48
+ PYTHONPATH=src python3 -m spiceguard FILE...
49
+ ```
50
+
51
+ ---
52
+
53
+ ## ngspice path resolution (priority order)
54
+
55
+ spiceguard locates the ngspice binary in this order:
56
+
57
+ 1. `--ngspice PATH` CLI flag — if given and not executable, raises an error immediately (no fallthrough)
58
+ 2. `$NGSPICE` environment variable — same hard-configured semantics; error if set but not usable
59
+ 3. `which ngspice` (PATH lookup)
60
+ 4. Legacy fallback `/opt/homebrew/bin/ngspice`
61
+
62
+ If none of the above resolves to a usable binary, spiceguard exits with code **3**
63
+ and prints a clear message listing what was tried. Install ngspice, or point to it
64
+ explicitly:
65
+
66
+ ```bash
67
+ spiceguard --ngspice /usr/local/bin/ngspice mynetlist.cir
68
+ # or
69
+ export NGSPICE=/usr/local/bin/ngspice
70
+ ```
71
+
72
+ ---
73
+
74
+ ## CLI usage
75
+
76
+ ```
77
+ spiceguard [--ngspice PATH] [--version] [--help] FILE...
78
+ spiceguard kicad [--ngspice PATH] FILE...
79
+ ```
80
+
81
+ Pass one or more netlist (or schematic) files. When multiple files are given,
82
+ spiceguard evaluates each in sequence and exits with the worst verdict across all.
83
+
84
+ ### Options
85
+
86
+ | Flag | Description |
87
+ |------|-------------|
88
+ | `FILE...` | One or more netlist or schematic files to check |
89
+ | `--ngspice PATH` | Explicit path to the ngspice binary |
90
+ | `--version` | Print version and exit |
91
+ | `--help` | Show usage |
92
+
93
+ ### Exit codes
94
+
95
+ | Code | Meaning |
96
+ |------|---------|
97
+ | 0 | TRUSTWORTHY — ngspice exited 0 and no trust issues found |
98
+ | 1 | FAILED — ngspice exited non-zero |
99
+ | 2 | SUSPECT — ngspice exited 0 but trust issues were detected |
100
+ | 3 | ngspice not found |
101
+ | 64 | Usage error (bad arguments) |
102
+
103
+ ### Example
104
+
105
+ ```
106
+ $ PYTHONPATH=src python3 -m spiceguard tests/netlists/n5_healthy_control.cir
107
+
108
+ ======================================================================
109
+ n5_healthy_control.cir
110
+ ======================================================================
111
+ ✓ TRUSTWORTHY (ngspice exit 0)
112
+
113
+ No trust issues detected.
114
+ ```
115
+
116
+ ```
117
+ $ PYTHONPATH=src python3 -m spiceguard tests/netlists/n1_missing_ground.cir
118
+
119
+ ======================================================================
120
+ n1_missing_ground.cir
121
+ ======================================================================
122
+ ⚠ SUSPECT (ngspice exit 0)
123
+
124
+ [FATAL] no_ground
125
+ → No node '0' (ground). SPICE has no voltage reference, so it may float the circuit and return arbitrary WRONG voltages with no error. Fix: tie a reference node to '0'.
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Input formats
131
+
132
+ | Extension | Handling |
133
+ |-----------|----------|
134
+ | `.cir`, `.net`, `.sp`, `.spice`, `.ckt` | Passed directly to ngspice |
135
+ | Any other netlist | ngspice natively translates KiCad, LTspice, PSpice, HSpice dialects |
136
+ | `.asc` | Converted in-process (experimental — see below) |
137
+
138
+ **LTspice `.asc` (experimental):** spiceguard converts `.asc` schematics using
139
+ built-in 2-pin symbol geometry (resistor, capacitor, inductor, diode, voltage
140
+ source, current source). Net connectivity is recovered by union-find over
141
+ wire/pin/flag coordinates. For anything beyond these built-in symbols, export
142
+ the netlist from LTspice ("View > SPICE Netlist") and feed that instead — that
143
+ path is exact. When a `.asc` is evaluated, the generated netlist is printed at
144
+ the end of the report so you can compare it against LTspice's own export.
145
+
146
+ ### Subcircuit support (Feature B)
147
+
148
+ spiceguard's parser handles:
149
+
150
+ - `.subckt` / `.ends` block collection and extraction
151
+ - `X`-instance flattening with automatic node namespacing (internal nodes become
152
+ `instancename:node` to avoid collisions)
153
+ - `+` continuation lines rejoined before parsing
154
+ - `.include` file resolution (local files only; URL `.include` lines are warned
155
+ and skipped)
156
+
157
+ Errors detected during subcircuit processing:
158
+
159
+ - `undefined_subckt` — X-instance references a subckt name not defined in the netlist
160
+ - `port_mismatch` — X-instance provides a different number of nodes than the subckt port list
161
+ - `subckt_recursion` — self- or mutual-referencing subckts (skipped with a warning)
162
+
163
+ ---
164
+
165
+ ## KiCad workflow (Feature C)
166
+
167
+ ```bash
168
+ spiceguard kicad myboard.cir
169
+ ```
170
+
171
+ The `kicad` subcommand runs the standard trust check **plus** a KiCad-specific
172
+ preflight that detects the classic export gotcha:
173
+
174
+ **`kicad_ground_not_zero`** — ngspice requires the circuit ground to be exactly
175
+ node `0`. KiCad schematics commonly use a `GND` net (or `GNDA`, `VSS`, `0V`,
176
+ `AGND`, `DGND`, `PGND`) that is NOT automatically mapped to node 0 on SPICE
177
+ export. When such a net is present and node 0 is absent, the simulation silently
178
+ floats the entire circuit, yielding wrong voltages with no error. The message
179
+ includes KiCad-specific fix instructions (set node mapping in Symbol Properties
180
+ or place a `PWR_FLAG`).
181
+
182
+ **Pipe form** — export and check in a single step without writing a file:
183
+
184
+ ```bash
185
+ kicad-cli sch export netlist --format spice myboard.kicad_sch -o - \
186
+ | spiceguard kicad -
187
+ ```
188
+
189
+ **Important:** spiceguard is a command-line workflow helper, not a native KiCad
190
+ plugin. KiCad has no post-simulation event API, so there is no in-GUI integration;
191
+ run `spiceguard kicad` from your terminal or CI pipeline.
192
+
193
+ ---
194
+
195
+ ## Verdict model
196
+
197
+ Every run produces one of three verdicts:
198
+
199
+ | Verdict | Meaning |
200
+ |---------|---------|
201
+ | **TRUSTWORTHY** | ngspice exited 0 and no trust-breaking issues found |
202
+ | **SUSPECT** | ngspice exited 0 but at least one FATAL/SILENT/WARN issue detected |
203
+ | **FAILED** | ngspice exited non-zero |
204
+
205
+ FAILED outranks SUSPECT, which outranks TRUSTWORTHY (this is why exit code 1 < 2
206
+ numerically but FAILED is the worst outcome).
207
+
208
+ ### Detectors
209
+
210
+ | Code | Severity | Description |
211
+ |------|----------|-------------|
212
+ | `no_ground` | FATAL | No node '0' — circuit has no voltage reference |
213
+ | `source_conflict` | FATAL | Multiple voltage sources forced across the same node pair |
214
+ | `no_dc_path` | FATAL | Node reachable only through capacitors/current sources — no DC reference |
215
+ | `timestep_collapse` | FATAL | Transient timestep collapsed; specific culprit identified from the log |
216
+ | `singular_node` | FATAL | Singular matrix at a node — no defined DC solution |
217
+ | `singular_branch` | FATAL | Singular matrix at a branch (current unconstrained) |
218
+ | `silent_fallback` | SILENT | ngspice exited 0 after gmin/source stepping **failed**; operating point is a relaxed estimate, not a true solution |
219
+ | `kicad_ground_not_zero` | WARN | KiCad export uses GND/GNDA/etc. but no node 0 (kicad subcommand only) |
220
+ | `dangling_node` | WARN | Node connects to only one pin — likely a wiring mistake |
221
+
222
+ ---
223
+
224
+ ## Security
225
+
226
+ spiceguard runs ngspice in batch mode on the netlists you give it. **Treat a
227
+ netlist like a script you are about to run**, because in two ways it is one:
228
+
229
+ - A SPICE netlist can contain `.control`/`shell` directives that execute
230
+ arbitrary shell commands. The captured log may include their output.
231
+ - `.include` reads whatever file path the netlist specifies (the same files
232
+ ngspice itself would read), so a hostile netlist can point at files on your
233
+ disk. The contents are parsed as a netlist, not printed.
234
+
235
+ **Only run spiceguard on netlists you trust.** It is a local dev/CI utility,
236
+ not a sandbox.
237
+
238
+ Hardening that *is* in place:
239
+
240
+ - ngspice is invoked with `--no-spiceinit`, so a `.spiceinit`/`spice.rc` config
241
+ planted next to the netlist is **not** auto-executed.
242
+ - The simulator is launched with a fixed argument list, **never** through a
243
+ shell (no `shell=True`), so the path can't be shell-injected.
244
+ - A 120s timeout terminates a hung/non-converging run (reported as FAILED, not
245
+ a crash); temp files use `mkstemp` (0600) and are always cleaned up;
246
+ subcircuit nesting is depth-bounded against stack exhaustion.
247
+ - spiceguard has **no third-party runtime dependencies**, and the source passes
248
+ `bandit -r src/` with no findings (`pip-audit` reports no vulnerable deps).
249
+
250
+ ---
251
+
252
+ ## Limitations (honest scope)
253
+
254
+ - **Parser scope:** The static netlist parser covers the element types listed in
255
+ `netlist.py` (`NODE_COUNT`). Exotic or simulator-specific elements not in that
256
+ table are silently skipped (they do not affect the running netlist — ngspice
257
+ still sees the full file).
258
+ - **`.asc` is experimental** and scoped to 6 built-in 2-pin symbol types. Any
259
+ schematic with transistors, op-amps, subcircuit blocks, or custom symbols must
260
+ be exported from LTspice first. Always verify the generated netlist against
261
+ LTspice's own "View > SPICE Netlist".
262
+ - **Legacy fallback** (`/opt/homebrew/bin/ngspice`) is tier-4 last-resort only.
263
+ It is not the preferred path; prefer `PATH` or the `$NGSPICE` variable.
264
+ - **KiCad integration** is a CLI helper, not an in-application plugin.
265
+
266
+ ---
267
+
268
+ ## Development
269
+
270
+ Run the test suite (no ngspice required for unit tests; integration tests
271
+ auto-skip when ngspice is absent):
272
+
273
+ ```bash
274
+ PYTHONPATH=src python3 -m pytest -q
275
+ ```
276
+
277
+ Observed output on the current suite:
278
+
279
+ ```
280
+ 82 passed in 3.93s
281
+ ```
282
+
283
+ ---
284
+
285
+ ## How we got here
286
+
287
+ 1. Audited the original image-to-sim idea (market / engineering / business).
288
+ 2. Deep research across the full EDA/sim workflow + a competition cross-check.
289
+ 3. Ruled out taken/funded gaps (AR debugging = Cadence inspectAR; AI autorouting
290
+ = Quilter et al.; SI/PI = heavy field-solver work).
291
+ 4. Landed on the SPICE result-trustworthiness wedge and built the tool.
@@ -0,0 +1,43 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "spiceguard"
7
+ version = "0.1.0"
8
+ description = "Can I trust this SPICE result? Catches silent/wrong ngspice simulation results."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { file = "LICENSE" }
12
+ authors = [{ name = "Moiz Lakkadkutta", email = "moizlakkadkutta1@gmail.com" }]
13
+ keywords = [
14
+ "spice", "ngspice", "circuit", "simulation", "eda", "electronics",
15
+ "kicad", "ltspice", "netlist", "verification", "convergence",
16
+ ]
17
+ classifiers = [
18
+ "Development Status :: 4 - Beta",
19
+ "Environment :: Console",
20
+ "Intended Audience :: Developers",
21
+ "Intended Audience :: Science/Research",
22
+ "License :: OSI Approved :: MIT License",
23
+ "Operating System :: OS Independent",
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3.9",
26
+ "Programming Language :: Python :: 3.10",
27
+ "Programming Language :: Python :: 3.11",
28
+ "Programming Language :: Python :: 3.12",
29
+ "Programming Language :: Python :: 3.13",
30
+ "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)",
31
+ "Topic :: Utilities",
32
+ ]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/moiz-lakkadkutta/circuit"
36
+ Repository = "https://github.com/moiz-lakkadkutta/circuit"
37
+ Issues = "https://github.com/moiz-lakkadkutta/circuit/issues"
38
+
39
+ [project.scripts]
40
+ spiceguard = "spiceguard.cli:main"
41
+
42
+ [tool.setuptools.packages.find]
43
+ where = ["src"]