Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github-workflow-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: Run verify on each project
run: |
for proj in projects/*/; do
python3 ~/electronics-stack/scripts/verify.py "$proj" --erc --conn --power --pi
python3 "$GITHUB_WORKSPACE/scripts/verify.py" "$proj" --erc --conn --power --pi
done

- name: Run KiBot pipeline
Expand Down
37 changes: 37 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Validate

on:
pull_request:
push:
branches:
- main

permissions:
contents: read

jobs:
tests:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out repository
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6

- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.12"

- name: Install test environment
run: python -m pip install -e '.[test,design]'

- name: Run repository and behavior tests
run: python -m pytest -q

- name: Compile Python sources
run: python -m compileall -q scripts mcp-server tests reverse-engineer

- name: Check shell syntax
run: |
bash -n test-corpus/download_all.sh
bash -n scripts/install_pre_commit_hook.sh
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ verify-out/
node_modules/
# Large artifacts and corpus runs
test-corpus/*/
test-corpus/download.log
corpus-results/raw_logs/
*.glb
*.pcb.json
Expand Down
62 changes: 62 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Electronics Stack agent guide

Electronics Stack is a local, on-demand KiCad verification and experimental
design toolchain. Start by reading `PROJECT.md`, then `CONTEXT.md`. Read the
relevant decision records under `docs/adr/` before changing behavior.

## Boundaries

- Treat source KiCad projects and downloaded corpus repositories as immutable
inputs. Write reports and generated designs to explicit output directories.
- Verification findings are engineering evidence, not proof that hardware is
safe, manufacturable, or production-ready.
- Keep default tests offline. Do not spend distributor quotas or download the
corpus during tests.
- Never commit credentials, provider responses containing account data,
local caches, generated dependencies, or host-specific absolute paths.
- Provider and external-tool failures must be explicit. Do not silently turn a
missing dependency, blocked site, or exhausted quota into a passing result.
- Preserve the historical pipeline reports. Correct stale status in a new
dated section or follow-up report rather than rewriting prior observations.

## Commands

Create an isolated environment before running the full suite:

```bash
python3 -m venv .venv
.venv/bin/python -m pip install --upgrade pip
.venv/bin/python -m pip install -e '.[test,design]'
```

Validation:

```bash
.venv/bin/python -m pytest -q
.venv/bin/python -m compileall -q scripts mcp-server tests reverse-engineer
PYTHONDONTWRITEBYTECODE=1 .venv/bin/python scripts/verify.py --help
bash -n test-corpus/download_all.sh scripts/install_pre_commit_hook.sh
```

The repository contract can run without third-party packages:

```bash
python3 -m unittest tests.test_repository_contract -v
```

## Agent skills

### Issue tracker

Issues and PRDs live in GitHub Issues for `ReidSurmeier/electronics-stack`.
See `docs/agents/issue-tracker.md`.

### Triage labels

Use the five standard Matt Pocock triage roles. See
`docs/agents/triage-labels.md`.

### Domain docs

This is a single-context repository with `CONTEXT.md` at the root and
decisions in `docs/adr/`. See `docs/agents/domain.md`.
60 changes: 60 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Electronics Stack context

## Domain

Electronics Stack turns KiCad source and optional project metadata into
reviewable findings and generated artifacts. Its central concern is evidence:
which checks ran, what they observed, what could not run, and which external
assumptions remain.

## Glossary

- **Project** — one input directory containing a KiCad project and schematic.
- **Check** — one deterministic or explicitly external verification operation.
- **Finding** — a check result with severity, evidence, and an accountable
failure or skip reason.
- **Verification report** — the collected findings for one project and run.
- **Provider** — a distributor or component-data source queried under its own
credentials, quota, cache, and error semantics.
- **Cache** — local, rebuildable provider data that is never repository truth.
- **Corpus entry** — a third-party project described by the corpus manifest and
materialized outside Git history for a test run.
- **Integration wrapper** — a narrow adapter around an optional executable or
Python dependency such as KiKit, KiBot, or SKiDL.
- **Design draft** — generated schematic/BOM output that requires human review.
- **MCP adapter** — the stdio process translating tool calls into checks,
provider lookups, wrappers, and design-draft operations.

## Module map

- `scripts/verify.py` coordinates project discovery and selected checks.
- `scripts/sch_parser.py` reads KiCad schematic structure for local checks.
- `scripts/connectivity_audit.py`, `power_budget.py`,
`pi_dts_validator.py`, and `datasheet_pinmatch.py` produce findings.
- `scripts/*_client.py` implement provider-specific access and caching.
- `scripts/*_wrapper.py` isolate optional external-tool behavior.
- `scripts/design_pipeline.py` creates experimental design drafts.
- `mcp-server/server.py` exposes the toolchain through the MCP adapter.
- `test-corpus/manifest.csv` records corpus provenance; the corpus clones are
inputs, not repository contents.

## Invariants

1. Source projects are read-only inputs unless a separate task explicitly
authorizes a source change.
2. Default tests are hermetic and do not query providers, download the corpus,
spend quota, or require a desktop.
3. Every skipped or unavailable check retains its reason in the report.
4. Credentials come from host configuration and never enter Git history.
5. Generated designs are design drafts, never manufacturing authority.
6. Provider caches and generated output are rebuildable and stay outside
tracked source.
7. The MCP adapter is local and on demand; it has no implied service uptime.

## Out of scope

- Declaring a board electrically safe or production-ready
- Automatically ordering components or fabrication
- Scraping providers in violation of their access controls
- Treating third-party corpus repositories as maintained source
- Operating a public web service
39 changes: 39 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Contributing

Read `AGENTS.md`, `PROJECT.md`, `CONTEXT.md`, and relevant ADRs before changing
behavior.

## Workflow

1. Create an isolated environment and run the repository contract.
2. Write a behavioral test that fails for the missing capability.
3. Implement the smallest change that makes it pass.
4. Refactor only while the focused and full suites remain green.
5. Update current-state docs and create an ADR for hard-to-reverse decisions.

Default tests must stay offline and must not download corpus projects, call
providers, spend quota, require credentials, or depend on a desktop session.

## Adding a provider

- Keep credentials, cache behavior, request details, and error translation
inside the provider module.
- Return a normalized result through a narrow interface.
- Add fixture-backed tests for success, unavailable credentials, provider
rejection, malformed data, and cache behavior.
- Wire the provider through both the MCP schema and handler.
- Document quota cost and opt-in live validation.

## Adding an integration wrapper

- Validate paths before invoking the external tool.
- Return explicit unavailable, skipped, success, and failure states.
- Put generated artifacts in the caller's output directory.
- Test missing executables and invalid inputs without installing the tool.
- Keep desktop-dependent behavior out of default CI.

## Pull requests

Keep each pull request independently reviewable. Include the red test, the
green implementation, validation commands, capability limits, and any
follow-up issue that remains.
152 changes: 67 additions & 85 deletions INSTALL.md
Original file line number Diff line number Diff line change
@@ -1,94 +1,76 @@
# Electronics Stack — Install Status

Host: Linux Ubuntu 24.04, Python 3.12.3, KiCad 9.0.8 (PPA).
Last updated: 2026-04-30.

## Tool Status

- **KiBot** 1.8.5: installed (pip --user, reinstalled with `--no-compile` to fix macros plugin loader) — verify: `kibot --version`
- **KiAuto** 2.3.7: installed (pip --user) — verify: `python3 -c "import kiauto"` (no `__version__` attr; package present)
- **KiBot apt deps**: installed — `xvfb`, `imagemagick` 6.9.12, `poppler-utils` 24.02.0, `ghostscript` 10.02.1, `pandoc` 3.1.3
- **InteractiveHtmlBom** 2.11.1: installed (pip --user, CLI: `generate_interactive_bom`) — verify: `xvfb-run generate_interactive_bom --help` (needs DISPLAY, use xvfb-run)
- **Kiri**: cloned at `~/electronics-stack/tools/kiri` — SKIPPED auto-install. `install_dependencies.sh` installs apt+opam+kicad-via-apt and could conflict with the PPA KiCad. Manual install steps:
```
sudo apt-get install -y build-essential libgtk-3-dev libgmp-dev pkg-config opam zenity librsvg2-bin imagemagick xdotool rename
bash -c "INSTALL_KIRI_REMOTELLY=1; $(curl -fsSL https://raw.githubusercontent.com/leoheck/kiri/main/install_kiri.sh)"
# Add to ~/.bashrc:
# eval $(opam env)
# export KIRI_HOME=$HOME/.local/share/kiri
# export PATH=$KIRI_HOME/submodules/KiCad-Diff/bin:$KIRI_HOME/bin:$PATH
```
Skip reason: opam install pulls a multi-hundred-MB OCaml toolchain; user should opt in.
- **kicad-happy**: NOT auto-installable from sub-agent. Run inside Claude Code session: `/plugin marketplace add aklofas/kicad-happy`
- **Docling** 2.92.0: installed (pip --user, ~500MB IBM models will download on first use) — verify: `docling --version`
- **Ki-nTree** 1.2.1: installed via clone at `~/electronics-stack/tools/Ki-nTree` (PyPI publishes only Python 3.9–3.11 wheels; we ran `pip install .` from source which works on 3.12) — verify: `kintree --help` (DB server required for actual use)
- **PySpice** 1.5: installed (pip --user) — verify: `python3 -c "import PySpice; print(PySpice.__version__)"`
- **ngspice**: pre-installed (apt) — verify: `ngspice --version`
- **ERCheck**: SKIPPED — does not exist as a public project. KiCad's built-in ERC + KiBot's `erc` preflight (uses `kicad-cli sch erc`) covers this.
- **Nexar Design Render Demo** (`NexarDeveloper/nexar-design-render-demo`): cloned at `tools/nexar-design-render-demo` — `.NET 6 / WinForms / OpenTK`, **Windows-only**, NOT runnable on Linux (`dotnet` not installed; `net6.0-windows` target). The reusable bits — GraphQL queries (`Nexar.Client/Resources/Queries.graphql`) and the OAuth flow against `identity.nexar.com` — were ported into `scripts/nexar_render.py` (Python 3, headless CLI, talks straight to `api.nexar.com/graphql`). The full demo lives on disk only as a reference for the schema and the line-inflation/tessellation algorithms, in case 2D primitive rendering is ever wanted.

### Nexar Design API setup

`scripts/nexar_render.py` uses the same `NEXAR_CLIENT_ID` / `NEXAR_CLIENT_SECRET`
as `octopart_client.py`, but requires a **different OAuth scope**:

- `octopart_client.py` -> scope `supply.domain` (Digikey/Mouser/Octopart parts)
- `nexar_render.py` -> scope `design.domain` (Altium 365 PCB primitives + 3D mesh)

If the existing Nexar app at portal.nexar.com only has Supply, you must:

1. Sign in to https://portal.nexar.com
2. Open your app's **Permissions** tab
3. Add the `design.domain` scope and accept the developer agreement
4. (No code change — the token cache will refresh on next call)

The two clients keep separate token files
(`~/.cache/electronics-stack/octopart/token.json` vs
`~/.cache/electronics-stack/nexar_design/token.json`) so the same app can hold
both scopes simultaneously.

**Nexar Design API caveat:** the API operates on **Altium 365 cloud workspaces**
exclusively. KiCad project uploads are not accepted. For pure-KiCad rendering,
use `kicad-cli pcb export step|glb` instead — that path is independent of Nexar.

Verify (will cleanly error if creds/scope are missing):
```
python3 ~/electronics-stack/scripts/nexar_render.py workspaces
```
# Electronics Stack installation

## KiBot Smoke Test
## Current workstation evidence

Config: `~/electronics-stack/kibot/sample.kibot.yaml` (schematic-only safe; uses kicad-cli-driven outputs).
Audit date: 2026-07-31.

Stock KiCad demo (`/usr/share/doc/ngspice/examples/osdi/hicuml0/KiCad`):
```
cd /tmp/kicad-demo
kibot -c ~/electronics-stack/kibot/sample.kibot.yaml --skip-pre erc -d /tmp/kibot-test
```
Result: PASS — produced `docs/ECL-OR-schematic.pdf` (70 KB) + `docs/ECL-OR-schematic.svg` (247 KB).
The current WSL host did not expose the following executables on `PATH`:

User's `Phone_Project`:
```
cd /home/reidsurmeier/KiCad/projects/Phone_Project
kibot -c ~/electronics-stack/kibot/sample.kibot.yaml -e Phone_Project.kicad_sch --skip-pre erc -d /tmp/kibot-test
```
Result: FAIL with `Missing argument 2 in 'pin name'`. KiBot's strict internal SCH parser rejects KiCad 9 multi-line pin syntax `(name "X") (number "Y")` without `(effects ...)` child elements (used in custom symbols `Phone_Project.kicad_sym`, `RASPBERRY_PI_ZERO_2_W.kicad_sym`, etc.). This is a kibot parser limitation, not an install issue. Workaround: rewrite custom symbols with full `(name "X" (effects (font (size 1.27 1.27))))` form, or upstream-report.
- `kicad-cli`
- `kibot`
- `kikit`
- `generate_interactive_bom`
- `ngspice`
- `kintree`
- `docling`

## Notes / Action Items
The system Python also lacked the repository's declared test dependencies,
including MCP, OpenPyXL, pdfplumber, RapidFuzz, ReportLab, sexpdata, and SKiDL.
This means a clean environment must be installed before the historical
pipeline results can be revalidated on this host.

- KiCad 9 PPA install is missing `/usr/share/kicad/symbols/` and a default system `sym-lib-table` — kibot/kiauto warn at runtime but workarounds via `KICAD9_SYMBOL_DIR` env var or manual table creation. Look into `kicad-symbols` apt package if needed.
- For real ERC on user projects, run `kicad-cli sch erc Phone_Project.kicad_sch -o erc.json` directly — bypasses kibot's strict parser entirely.
- Plugin install (`/plugin marketplace add aklofas/kicad-happy`) is user-action.
- IBOM CLI requires X (`xvfb-run generate_interactive_bom ...`).
## Python environment

## Tooling Layout
Create an isolated environment:

```bash
python3 -m venv .venv
.venv/bin/python -m pip install --upgrade pip
.venv/bin/python -m pip install -e '.[test,design]'
.venv/bin/python -m pytest -q
```
~/electronics-stack/
INSTALL.md # this file
kibot/sample.kibot.yaml # smoke-test config
tools/
kiri/ # cloned, NOT installed (manual opam toolchain needed)
Ki-nTree/ # cloned, installed via `pip install .`
InteractiveHtmlBom/ # not cloned, pip-installed system-wide
```

The base dependencies cover the deterministic Python checks and MCP adapter.
The `design` extra installs SKiDL. KiCad, KiBot, KiKit, InteractiveHtmlBom,
ngspice, and other system integrations remain optional and must report an
explicit unavailable or skipped result when absent.

## External tools

External repositories are not committed as orphan Git links. Install or clone
them explicitly when their integration is needed:

| Tool | Purpose | Upstream |
| --- | --- | --- |
| KiCad | Canonical ERC and board tooling | <https://www.kicad.org/> |
| KiBot | KiCad output automation | <https://github.com/INTI-CMNB/KiBot> |
| KiKit | Panelization and fabrication output | <https://github.com/yaqwsx/KiKit> |
| InteractiveHtmlBom | Assembly visualization | <https://github.com/openscopeproject/InteractiveHtmlBom> |
| Ki-nTree | Part and inventory integration | <https://github.com/sparkmicro/Ki-nTree> |
| Kiri | Visual KiCad history comparison | <https://github.com/leoheck/kiri> |
| Nexar render demo | Design API reference implementation | <https://github.com/NexarDeveloper/nexar-design-render-demo> |

## Provider configuration

Provider credentials belong in host configuration with mode `0600`, never in
the repository. Network validation is opt-in because provider quotas and
access conditions vary. `--with-api` permits distributor API calls, while
`--with-browserbase` independently permits managed-browser escalation. The
default sourcing command can still issue ordinary HTTP requests to URLs in the
BOM; it does not use either provider mechanism. See ADR 0002.

The LCSC client is offline during normal construction. Populate or refresh its
jlcparts database only through the explicit refresh operation documented by
`scripts/lcsc_client.py`; importing the client never downloads data.

Nexar Supply and Design APIs use different OAuth scopes and separate caches.
The Design API operates on Altium 365 projects; it does not accept KiCad
uploads. Pure-KiCad rendering must use KiCad's own export tools.

## Historical note

The previous version of this document recorded successful installs on
2026-04-30, including KiCad 9.0.8, KiBot, KiKit, InteractiveHtmlBom, Docling,
Ki-nTree, PySpice, and ngspice. Those observations describe an earlier
environment and are not current-host verification. Recover that revision from
Git history when exact historical commands or failure text are needed.
Loading