v0.1.0 is delivered. The `boxsim` package decodes Boxsim containers in pure Python and generates KiCad 10 projects from their crossovers; see `README.md` for the format and the tool, and `CHANGELOG.md` for what changed.
Environment as of 2026-09-29: KiCad 10.0.6 installed natively, Python 3.13.15, Visaton library checked out at `/home/magdev/workspaces/other/kicad-visaton-lib` from `ssh://git@src.bundespruefstelle.ch:2022/magdev/kicad-visaton-lib.git`, upgraded to the KiCad 10 format as part of this work.
Reference project for all work: `boxsim_dl/BoxSim 2.1 DE (Stand 12.2025)/Projekte/Default.BPJ`.
## v0.1.0 - `boxsim` CLI and KiCad project generation
Delivered 2026-09-29. All seven tickets landed; what follows records the outcome, including where it differed from the design.
### What shipped
-`boxsim` with `decode`, `extract`, `netlist` and `build`, installable and packaged as a `python:3.13-slim` image bundling the Visaton library and catalogue.
- A pure Python A2F codec. `Boxsim.exe` and Unicorn are gone from the shipped tool and the image.
- Grid to netlist extraction, catalogue-driven symbol resolution, KiCad 10 schematic generation preserving the Boxsim layout, and a netlist-equality verification gate.
### Measured results
| Gate | Result |
| ---- | ------ |
| Codec vs Boxsim's own decoder | 134 containers byte-identical, 270 pre-2.0 files rejected by both |
| Decode time for `Default.BPJ` | 69 s to 14.5 s (hook binding) to 1.6 s (pure Python) |
| Container vs host build | `.kicad_sch` and `.kicad_pro` byte-identical |
| Tests / lint | 32 passing, ruff clean |
### Where it differed from the design
- **The library needed changing after all.** The plan said no library change was required. That was wrong: the KiCad 6 era syntax (`(id N)` properties, bare `hide` tokens) cannot be embedded into a current schematic's `lib_symbols` cache. `kicad-visaton-lib` was upgraded with `kicad-cli sym upgrade` and `fp upgrade`, and the tool now refuses a legacy library with an error naming the fix. A pre-existing broken footprint reference on the `MKT 22μF` symbol (`Visaton:MKT-Kondensator`, which never existed) was repointed at `MKT 22mF`; all 48 footprint references now resolve.
- **Derived symbols had to be flattened.** 19 of the 98 symbols use `(extends ...)`, and KiCad never writes `extends` into a schematic cache. Pin geometry follows the chain to the base symbol, and the base's graphics are inlined under the derived name.
- **The decoder needed a padding allowance.** Boxsim reads past the end of its staging buffer for the final symbol's 16 bit lookup window. Five files failed until the reader tolerated a bounded number of zero padding bytes; the bound keeps genuine truncation loud.
- **Rotation could not be assumed.** A symbol's origin is not its pin midpoint (`Amplifier` has both pins at px=3.81), and a symbol's pin axis does not follow its slot. Rotation is chosen per symbol from its own pin axis, the origin is derived so the transformed pin midpoint lands on the slot midpoint, and the four transform matrices were measured against KiCad's demo schematics rather than guessed.
- **The image self-check was decorative at first.** `cross_check` only proves the catalogue is a subset of the library, so a truncated catalogue passed it and would have shipped an image rejecting valid parts. `catalog.check_shape` now asserts the catalogue's full shape, and the failure was reproduced deliberately before the fix was accepted.
- **Grid pitch is 25.4 mm, not 20 mm.** The widest symbol spans 22.86 mm between pins, so 20 mm would overlap neighbouring cells. `Default.BPJ` therefore lands on A3 rather than A4.
### Ticket outcomes
- **BOX-0** emulator bound to trap addresses, moved to `boxsim/_oracle.py`, reachable as `boxsim decode --oracle`.
- **BOX-1** `boxsim/a2f.py`, differentially tested by `tools/difftest.py`, self-test proves the comparison detects a corrupt digest.
- **BOX-2** `boxsim/model.py`, slot partition covered by tests, netlist cross-checked against crossover physics (600 Hz and 5.7 kHz corners).
- **BOX-3** `boxsim/catalog.py` and `boxsim/symbols.py`.
- **BOX-4** `boxsim/schematic.py`.
- **BOX-5** `boxsim/cli.py` and `boxsim/project.py`.
- **BOX-6** `boxsim/verify.py`, with `test_gate_has_teeth` mis-wiring a component to prove the gate fails when it should.
- Multi-channel projects (`nAmps` up to 6), probably one hierarchical sheet per channel.
- Active filter sections (`#AktFilter<n>`, `#Aktfilter<n>`), which Boxsim models as DSP biquads with no passive equivalent.
- Per-driver symbols instead of the generic `Speaker`, if the library grows them. The 400 `.BCH` files provide the model names and TSP data.
- Round trip: patch component values in a `.bpj` from an edited schematic. Needs the A2F compressor, of which only the decompressor is currently exercised.