All notable changes to Signex ship here. Format follows Keep a Changelog and Semantic Versioning, both strictly.
- Release heading:
## [X.Y.Z] — YYYY-MM-DD, newest first, directly under## [Unreleased]. Never— unreleasedon a section you intend to tag. - Section heading:
### <Type> — <scope>, where<Type>is one of Keep a Changelog's six — Added, Changed, Deprecated, Removed, Fixed, Security. The scope suffix is free text (### Fixed — netlist); drop it when the section covers the whole release (### Changed). Lead with the type so a reader scanning for regressions finds everyFixedwithout reading section titles. - No
####. Sub-scope goes in the suffix after a colon:### Added — footprint editor: sketch constraints. - Note sections are allowed alongside the types, because they record something other than a change:
### Breaking changes,### Deferred to <version>,### Constraints — …,### Upgrade notes,### Known issues / workarounds,### Issues closed,### Provenance,### Versions affected. Anything that is a change takes a type instead — don't reach for a note heading to avoid choosing one. - Close every section with
[Full changelog](https://github.com/alplabai/signex/compare/v<prev>...v<this>), where<prev>is the previous tag — not the previous section. v0.12.0 and v0.6.2 have no section, and v0.6.2 has a tag with none.
Each release section is authored before the vX.Y.Z tag is created, so the release workflow picks it up as the GitHub Release body. .github/workflows/release.yml ("Extract CHANGELOG entry for this tag") matches ## [X.Y.Z] against the tag with the leading v stripped — so that heading line is load-bearing and must not be reworded.
Nothing enforces this. A missing section is not an error — the workflow silently falls back to auto-generated notes, so a tag cut without a section still ships, just with a worse release page. This file previously cited .claude/hooks/pre-release-guard.sh as the enforcement; that path is gitignored and no such hook exists.
- GPU scene render path — the PCB editor canvas can render traces, pads, vias, and zones through the
signex_gfxwgpu pipelines via iced'sshaderwidget instead of CPUcanvas::Frametessellation. Gated behindfeature_flags::PCB_GPU_RENDER(defaultfalse) plus a Preferences toggle; the CPU path stays the default until GPU visual parity is confirmed on hardware. - CPU↔GPU draw-order parity lock — a shared
signex_gfx::scene::ordermodule defines the canonical bucket draw order both paths walk, so neither path can drift silently.
- GPU concave polygon fill — the polygon pipeline fanned every contour from its first vertex, which is exact only for convex shapes; a concave copper pour/rule-area bridged triangles across the notch and painted copper where the pour had none.
append_fillnow triangulates viasignex_sketch::ear_clip, matching the CPUframe.fill(lyon) result exactly (pinned by a shoelace-area regression test). Found and fixed a latent bug inear_clipitself while wiring it up: a bridge edge created by clipping one ear could pass exactly through another still-remaining vertex, letting the algorithm approve an invalid ear and collapse the rest of the polygon into a self-intersecting remainder —is_ear's occlusion test now also blocks on a non-corner vertex sitting exactly on the ear's boundary. - GPU overlay z-order —
gpu_scene()folded overlay geometry (active-layer zone highlight, selection highlight, DRC markers, ratsnest) into the basepolygons/lines/circlesbuckets, which the GPU draws before other base content — so an overlay meant to sit on top rendered underneath it. Overlays now stay in their own buffers and composite in a dedicated pass strictly after every base bucket, matching the CPUdraw_sceneoverlay pass. - GPU dashed lines —
line.wgsldeclared the per-instancestylefield but never consumed it, soRATSNEST_STYLE_DASHED/ keepout / DRC-overlay segments rendered solid on the GPU. The fragment shader now derives a dash/gap pattern fromcamera.mm_per_px(reproducing the CPU's literal 8px dash / 5px gap at any zoom) and discards the gap portions. - GPU text now pans with the view — glyph text rasterised for the GPU path was scaled by zoom but never translated by the pan offset, so labels drifted off the geometry they annotate on any pan; the screen-space pan term is now applied.
- Bounded GPU buffer growth — the line/circle/arc/polygon instance buffers are clamped to the device
max_buffer_sizebefore reallocation, so a pathological board degrades to a truncated draw instead of panicking the render thread. - Preferences dirty-tracking (review #308 findings 1-2) —
preferences_draft_differs()compared only the 7 appearance drafts, so theme import (a same-tagcustom_themecontent swap), a pending keymap rebind, or a component-class edit could get silently clobbered back to "clean" by the next unrelated appearance-draft change, letting Close discard it with no prompt. Apreferences_dirty_stickyflag now pins the one imperative edit the comparator structurally cannot see (theme import);preferences_has_unsaved_changes()(sticky || comparator) is the single source every dismiss route checks. Every dismiss route — the in-dialog X, Esc, and an OS close request (Alt+F4 / native ✕) on the detached Preferences window — now refuses to close while dirty instead of silently reverting.
- GPU glyph atlas now trims each frame, and GPU text prep/draw failures log once instead of being swallowed silently.
- Removed the dead
SCHEMATIC_GPU_RENDERflag — it gated no live path (the schematic GPU adapter is compiled and tested but not yet mounted). - One GPU scene renderer, not two —
schematic_shaderwas a second, unmounted copy ofscene_shader's five pipelines that had drifted apart from it on upload skipping, overlay compositing, draw order and error reporting; mounting it as written would have deep-copied the wholeSceneevery frame and dropped every overlay. It is gone, andscene_shaderis now parameterised by aSceneSurfacemarker so each editor surface gets its own pipeline slot — iced keys a stored pipeline by the primitive'sTypeIdin one process-wide map, so two surfaces sharing a type would overwrite each other's instance buffers and camera. The deleted copy held the only test in the tree pinning the glyph-atlastrimoverride; that guard now watches the pipeline that actually renders. - Preferences draft-seeding deduplicated — opening and reverting the dialog now share one
seed_preferences_drafts_from_live()helper instead of two independently-maintained copies of the same 8 assignments, closing the add-a-field-to-one-but-not-the-other drift hazard. The native-close revert backstop is now gated on the dirty flag (skips the keymap-profile rebuild + canvas cache clears on a no-op close), the double content-cache invalidation inrevert_preferences_draftsis gone, and boot reads the persisted GPU-render preference once instead of three times.
The v0.15.0 "Editor Fixes" milestone — 43 commits, 2026-07-15 → 2026-07-23.
Footprint- and symbol-editor correctness, an order-independent netlist
derivation with the ERC/BOM consumers reading the same topology, crash-safe
persistence, and CI/toolchain hardening (pinned Rust 1.97.0, an enforced
rustfmt gate, and the god-file ratchet).
-
Netlist derivation no longer depends on wire order (#402).
uf::unionnow keeps the smaller root, so a class's representative is a pure function of the partition rather than of the order the unions were applied in, andanchor_pointresolves a label tapping several wire interiors by a total order over the segments instead of by whichever the slice yielded first. -
Junction dots are reconciled on every command that changes wire geometry (#402).
PlaceWireSegmentreconciled both directions of a T;MoveSelection/RotateSelection/MirrorSelectionmutate the same coordinates and reconciled nothing, so dragging a stub onto a trunk left a junction-less T — which the netlist deliberately reads as disconnected (#107) — and the connection was silently lost. -
Junction autoplacement no longer mints a dot the netlist will not honour (#402). The geometry helpers work in
f64mm with a 0.01 mm tolerance, butSheetConnectivityhonours a junction only where the point is exactly collinear in the 1 µm key space. A candidate a few µm off a wire produced a dot asserting a connection the derivation refused to make. Every minted dot is now gated on the netlist's own predicate; off-grid geometry gets no dot rather than a lying one. -
Same-name label merging is applied by every connectivity consumer (#404).
SheetConnectivity::merge_named_labelsis now called bysummarize_nets(ERC/DSL),net_label_conflict,missing_power_flag, andflood_net_elements(the net-colour highlight), so all of them derive the same topologybuild_netlistdoes. Two physically disjoint wires sharing a label name were previously separate nets to each of these, but one net to the netlist.Behaviour change for existing projects. Merging changes the net name the ERC DSL sees, not just the net count: when a
VCC-labelled fragment merges with one carrying a higher-priorityGlobal/Powerlabel, the merged net takes the higher-priority name. A DSL rule keyed onnet.name == "VCC"can therefore stop matching entirely rather than merely matching once instead of twice.net_label_conflictalso now reports conflicts created by such a join — two differently-namedNetlabels pulled onto one net by a sharedGlobal/Powerlabel previously went unreported, and both signal names were silently dropped from the netlist.
-
Auto-generated
N$knet names change for existing schematics. Making the partition order-independent reorders the sorted roots that assignNetId1..=N, and unlabelled nets are named from that id. An unchanged schematic therefore exports a netlist whoseN$knames differ from before this release. This is a one-time, unavoidable consequence of the fix — the alternative is keeping net numbering dependent on document order.Constraint this records for later:
net_nameis a persisted field of the PCB format (on pads, tracks and zones), so any future schematic → PCB net sync must match by terminal set, not by auto-generatedN$kname. Nothing in-tree feedsbuild_netlistintosignex-pcbtoday, so no routed copper is remapped by this change.
AmbiguousLabelAnchor(warning). A label sitting where two or more wires cross with no junction is anchored to exactly one of them by the tiebreak above. The answer is deterministic but not predictable from the geometry, so ERC now says the net was decided by tiebreak instead of leaving the user to guess. Labels on a wire endpoint, or on a dotted crossing, are not flagged.
- Real Align… dialog composing the existing
AlignOps(#370). - Break Track wired to the sketch
split_lineprimitive (#372), backed by a newsplit_lineprimitive that divides aLineat parameter t (#429). - Drag Track End as an endpoint-biased segment grab (#361).
- Sketch active bar grouped under Create / Modify, with the chrome search bar centred (#443).
- Context-menu selection no longer acts on a stale target, and no-op edits stop dirtying the document or pushing history (#146).
- Pad geometry — pad rotation is now real geometry that holds the sidecar re-mint invariant (#433); a pad's ownership of its sketch geometry survives reopen (#424); the Place/Move active-bar button drives the pad Select tool (#427).
- Shapes dropdown collapses the three phantom Arc rows to one (#462).
- Tab during footprint sketch placement routes to the sketch instead of triggering Save in the standalone editor (#428).
- Child-sheet references resolve parent-relative through one shared helper (#339) and are rejected when they escape the resolution root (#473).
- Sheet delete completes correctly, and cut/copy is gated on a preservable selection subset (#425).
- Annotate preview and action now agree on sheet-walk order (#470).
- Deterministic child-sheet ownership with loud cross-directory filename collisions (#459).
- Stale minted junction dots are removed on wire-geometry reconcile (#480).
- ERC merges same-name labels in the shared connectivity so it matches the netlist (#418).
- Export gained a project sheet-set assembler with per-deliverable stitch severity, plus the Export-anyway netlist path (#436, #406, #431 via #449).
- Designators order naturally with Fitted ranked above DNP (#419), and field lookup is deterministic and case-insensitive with base fit resolution (#474).
- Save-All routes every dirty document kind and surfaces the failure reason (#452), covered by Cut/Save-All regression tests (#460).
- Crash-safe writes — table and settings writes go through
atomic_write(#421), whose per-writer tmp sibling name is now unique (#469). - Schematic extras serialize deterministically (#409).
- The test suite no longer touches the real
prefs.json; the no-config-dir fallback is surfaced (#439).
- Circumcircle math unified — four copies collapsed to one canonical helper (#461), with the SVG output routed through it (#484).
- Bake/library rejects unit-suffixed chamfer ratios and fixes a byte-boundary panic in hash-cell parsing (#417).
- Rust pinned to 1.97.0 (
rust-toolchain.toml+ CI) for cargo/CI/fmt parity (#454), the MSRV raised to match (#456), and the pin documented (#455). rustfmtis now an enforced gate —rustfmt --allacross the workspace and the fmt job made blocking (#453).- God-file ratchet gate — no new or growing production god-files (#450), the
cap raised to 1000 lines (#451), counting production lines past an external
mod tests;(#468). - Data-driven dropdown table replaces the
dropdown.rsgod-file (#458). - The labeler matches
signex-apptests so test-only PRs get area labels (#441).
Everything since v0.13.0 — 221 commits, 2026-05-06 → 2026-07-18.
This section was originally written on 2026-05-31 covering only the footprint
editor, and never tagged. Work kept landing past it: symbol multi-unit, the
signex-net netlist contract, keyboard-shortcut profiles, the schematic GPU
render path, and 83 commits of ADR-0001 decomposition. Rather than mint a
phantom version — v0.12 is already one, planned and merged but never tagged —
v0.14.0 claims all of it. The bulk below was reconstructed from git log and
summarises by theme rather than listing every commit.
signex-netcrate — the authoritativeNetlistcontract (#137). One derivation of connectivity, consumed by everything that needs it instead of each subsystem rolling its own.- Cross-sheet netlist stitching —
build_project_netlist(#168). - Same-name labels merge into a single net in
build_netlist(#154). - Netlist contract completed and connectivity-gate defects fixed (#157);
ERC now consumes the shared derivation, with one
SymbolTransform(#158). - Project-netlist app wiring — cache, shared sheet-map, exporter (#159).
- Multi-unit parts as a first-class concept —
part_count, unit buttons, band→footer layout (#290–#292), per-unit graphics viaSymbolGraphic.part_number(#293), and per-unit body geometry, with draw, hit-test, and select all scoped to the active unit (#294). - Drawing tools — two-click line, two-click circle, three-click arc, two-click rectangle with per-graphic fill (#299), rectangle edge handles with resize cursor hints.
- Selection — AutoCAD-style rubber-band box select, select-all (Ctrl+A), clicking a graphic body drags the whole symbol, and clicking a graphic selects only that graphic.
- Undo/redo and grid controls; separate grid styles for schematic and symbol editors; Properties panel refreshes on canvas selection.
- Optional pin grab-by-label with whole-pin glow — per-tab toggle, default off (#298).
anchor2d— pivot-aware 2D rotation with a compensated (B-type)Transform2D.- Polygon graphic primitive + closed-shape authoring (#378) — a
SymbolGraphicKind::Polygon(implicitly-closed ring) with fill/stroke, concave-correct hit-test, and per-vertex handles; a Place Polygon click-collect tool with the full close-gesture set; Join into Polygon, which chains selected lines/arcs end-to-end into one closed polygon (auto-closing an open chain) in a single undo step; and a right-click context menu built from a pure data-to-menu row function.
- Configurable keyboard-shortcut profiles with an in-app editor (#202, supersedes #116).
- Menu labels are sourced from the command table via
menu_label()(#270, #282), so menus and the shortcut pane can present differently from oneCommandMetadataentry (#271). - Drift-guard test: every menu command id must resolve in
CommandMetadata(#272, #283).
- Schematic GPU render module via iced's shader widget (#169, #200).
- Schematic renderer scene cutover started in the app canvases; Milestone F schematic-runtime tasks 03–05 completed.
signex-gfxaligned to iced's wgpu 27 + cryoglyph, dropping the dual GPU stack (#198).- Large-scale decomposition under ADR-0001 — 83 commits splitting god-files
and god-functions across
signex-app,signex-engine,signex-renderer,signex-gfx,signex-types,signex-library,signex-net,signex-output, and the 3D importer, under an 800-line cap. Includes the rootMessagenamespacing (D3), the canvasupdate/drawsplits, the property-panel family, and the 1,223-linecollect_overlays(#210). Internal only — no behaviour change intended. - License Guard is framed by GPL rather than by the banned tool's name (#209).
- Repo governance: label taxonomy, path labeler, CODEOWNERS, refreshed templates (#133); roadmap reconciled and milestones + tiers as code (#300).
- Data loss / persistence — TSV cells are escaped so a schematic save can
always reopen (#96, #130); C0 control bytes in a TSV cell are escaped too, so
a stray control character in user text can no longer produce a
.snxsch/.snxpcbthat will not reload (#386, #397); the footprint editor's STEP store is written viaatomic_write, so a crash mid-write can no longer strand a corrupt 3D asset that is then served forever (#387, #398); persistence made crash-safe withfsyncatomic_write, atomic.snxprj, and a corrupt-JSON guard (#104, #119); residual document writes routed throughatomic_write, New Project guarded (#104, #128); prompt for unsaved changes on app exit (#95, #124). - Footprint sketch-profile pads — moving a pad made with "Make Pad from
Profile" left its sketch profile behind, and the bake then resolved the
copper back to the pad's original location, so an exported footprint placed
the pad in the wrong spot with no warning. The profile now travels with the
pad: the loop walker gained an id-level core that needs no solve, a pad that
first appears from the sketch side is relinked to its
PadAttrentity by number, and a whole-pad drag no longer snaps its cursor to the pad's own outline vertices (#142, #311). - Connectivity — wires connect at T-junctions in net derivation (#107, #120); the net-colour flood runs on the authoritative connectivity core (#138).
- ERC agrees with the netlist on mid-wire taps — ERC re-derived
connectivity with endpoint-only checks, so a label or pin tapping a wire's
interior was invisible to the rules while the netlist saw it. The rules now
read the shared wire-anchored connectivity (#388, #399); DSL net names are
anchored the same way, so rules keyed on
net.name/net.classno longer see an unnamed net plus a phantom (#396, #403); and bus range labels placed mid-span — where they are normally drawn — are anchored to their bundle, so aD[0..7]/D[0..3]width mismatch on one bus is reported instead of silently passing (#395, #405). - PDF and preview net names — the exporters re-derived connectivity of
their own instead of reading the authoritative
NetlistoffExportContext, so an exported sheet could annotate a net differently from the netlist it shipped with (#389, #400). - Editing — Ctrl+C/X/V/D and shift-chorded shortcuts un-broken (#103, #127); Find/Replace replaces the matched substring rather than the whole field (#102, #125).
- Symbol rendering — pin text rotation corrected for screen Y-flip; name
h_alignreversed for flipped pin orientations; pin tip rotates around the body-end pivot; arc angles negated in both preview and scene renderer for the screen-space y-flip; arc discontinuity past ±180° prevented; rotation angles normalised;LineJoin::Roundfor rectangle/polygon corners; round line caps. - Symbol arc sweep convention unified, with a data-safe legacy migration
(#378) — the CPU canvas draw path was the lone signed-sweep holdout while
hit-test, the GPU SDF shader, and rotation all read
start_deg/end_degas a CCW sweep that wraps through 360°, so a rotated 0°-crossing arc drew its complement while clicks landed on the real arc. All consumers now route through one authority (signex_gfx::primitive::arc::ccw_wrapped_sweep_rad), a full-turn arc draws and hit-tests as one circle, and a load-time migration self-heals legacy clockwise-signed pairs on read. Every endpoint writer (placement, rotation, the Properties panel, and the arc-endpoint drag handle) normalises into[0, 360)so a saved arc round-trips instead of silently reloading as its complement. - Interaction — unsnapped cursor position for Select-tool hit-testing,
snapped coords retained for drag anchor and delta;
CursorAtpublished during box-select drag to force redraw. - Detached windows open in front — a detached modal, undocked tab, or detached panel opened at the default window level with no raise, so on Windows it could appear behind the main window and the user had to move the main window to find the dialog they had just opened (#311).
- Preferences modal is responsive to window resize (#208) and the reopen regression is fixed; theme-aligned canvas backdrop, sheet tracks the stored paper size (#201); library server gains a persistent DB backend and rejects duplicate-row POST (#97, #122).
- lavapipe installed so
signex-gfxGPU smoke tests run headless (#126). cargo-denyadvisories are informational, not a merge gate (#123).- CI and license guards run on
trunk(#121); PR preconditions aligned with the org control-process convention (#134); Linux dependency install hardened against thepackages.microsoft.comapt outage (#155). - Declared the 1.88 MSRV and corrected the README + CONTRIBUTING Rust version references to match (#383).
- README and codebase map refreshed to the v0.14 workspace reality (#385).
v0.13.0 shipped the footprint / sketch editor compiled but hidden behind a
feature flag while it was finished; v0.14 completes the remaining active-bar
tooling and enables the editor — opening a .snxfpt now opens an editable
tab, and the New Footprint / PCB Library create flow is live again.
- Nine more sketch constraints exposed in the sketch-mode active bar: Tangent (line-arc + arc-arc), Angle, Equal-Radius, Point-on-Arc, Distance-point-to-line, Distance-point-to-circle, and Symmetric (about a line + about a point). The Newton-LM solver and serialization already supported all 19 constraint kinds; this surfaces the 9 that had no button. Selection-first UX: select the entities, the valid constraint buttons light up. The two 3-entity Symmetric constraints take their third entity from the multi-select extra slot.
- Align / Distribute / Spacing (12 ops) — Align Left/Right/Top/ Bottom + center H/V, Distribute Horizontally/Vertically (equal centre gaps, extremes fixed), and Increase/Decrease H/V spacing (one grid step, pivoting about the selection centroid). Operate on the combined pad selection; no-op under 2 pads (3 for distribute). Sketch-backed pads mirror their move into the sketch; undo-snapshotted.
- Move / Drag / Move Selection — activate the Select tool (footprint
pad-move is drag-under-select). Adds a
nudge_padshelper +FootprintActiveBarNudgeSelectionfoundation for the typed-delta "Move Selection by X, Y…" dialog (dialog itself deferred to v0.15). - Fill / Solid Region / Text Frame — wired to the existing
filled-polygon (
PlaceRegion) and silk-text (PlaceString) place tools. - Selection-filter "All - On / All - Off" toggle wired to a new
SelectionFilter::set_all.
- Move Selection by X, Y… now opens a typed-delta modal (two mm inputs) that nudges the pad selection by the entered amount, reusing the same sketch-mirror + undo path as the one-step grid nudge.
- 3D Body / Extruded 3D Body active-bar items mint the footprint's
body_3d(extrude the courtyard, or the fab outline) — visible immediately in the CPU 3D preview. The interactive wgpu pipeline stays deferred (v2.x). - Text Frame is now a real bounding-box place tool: drag a rectangle to set the frame; the silk string aligns/clips inside it. (No auto-wrap yet.)
- Footprint-native selection-filter presets — the filter dropdown's
All-On/All-Off toggle and preset chips are wired to real footprint
SelectionFilterKindpresets, persisted underfootprint_filter_presetsin prefs, with a "Save current filter as preset" capture. (Replaces the schematic-typed presets that could not apply to footprints.)
- Pad shape-param leak —
mint_shape_geometry_fornow clears a pad'sshape_paramsbefore regenerating geometry, so changing a pad's shape (e.g. RoundRect → Round) no longer strands stale parameter keys for the solver / next bake.
FOOTPRINT_EDITOR_ENABLEDflippedfalse→true(crates/signex-app/src/feature_flags.rs). Theopening_snxfpt_does_not_create_editable_tab_when_gatedregression test branches on the flag and now asserts the enabled behaviour.
- Break Track / Drag Track End (need track-segment split infra).
- Zero
kicadsubstrings undercrates/; no third-party constraint-solver substrings undersignex-sketch/signex-bake;cargo-denyadvisories + licenses green; fullcargo test --workspacegreen (GPU smoke tests skip headlessly).
The v0.13 Symbol & Library milestone. This release pairs the
cleanroom schematic renderer (the work planned as v0.12) with a wave of
symbol-editor and library polish. There is no separate v0.12.0 tag — the
cleanroom rewrite ships here. The workspace version had already advanced
to 0.13.0, so this release adopts that number rather than bumping
backward.
- Clean-room reimplementation of
crates/signex-renderer/src/schematic.rs(label / symbol / field-style rendering) and the field-autoplace heuristic, executed against Signex-only specifications (docs/RENDERING_RULES.md, Altium parity goals, IEEE-Std-91) rather than any third-party EDA source. Schematic rendering output changes subtly versus v0.11 — label placement, field rotation/justification, and IEEE-Std-91 pin decorators are now driven by the documented rules. This is the milestone tracked as "v0.12" in prior READMEs.
- Unified active-bar widget. The
.snxsymeditor adopts the genericsignex_widgets::active_barin a single-call form, so the symbol editor's floating toolbar matches the schematic editor byte-for-byte (root highlight, dropdown panels, right-click, chevron). Newactive_bar_dropdownsmodule backs the per-tool dropdown overlays; dropdown panels position relative to the bar'sy_offsetso they open directly under their trigger button. .snxsymTOML+TSV envelope. Standalone symbol files serialise to the same TOML-header + TSV-bulk envelope as the rest of the Signex format family (SymbolFile::to_toml_string/from_bytes), so pin tables are line-diffable in git. Legacy JSON.snxsymfiles still load (auto-detected on open).
- Library-subsystem polish across the Library Browser, Component
Preview, and standalone primitive tabs: inline Pick Symbol / Pick
Footprint binding with read-only cells, project-tree listing of
.snxsym/.snxfptfiles (rather than individual primitives), Save-As flow for standalone primitive libraries, and assorted binding / refresh fixes.
- Per-file Git history right-dock panel refinements — the History
panel follows the active tab and renders the file's recent commits via
signex_widgets::history_pane, async-loaded with a generation counter to drop stale results on tab switch.
Changed — footprint editor hidden for this release
- The footprint / sketch editor is gated off in v0.13.0. It is
feature-incomplete and was under heavy daily iteration; rather than
ship an unfinished editor, its user-facing entry points are disabled
behind a compile-time flag (
signex_app::feature_flags::FOOTPRINT_EDITOR_ENABLED). Opening a.snxfptno longer pushes an editable Footprint Editor tab, and the "New Footprint / PCB Library" create flow is removed from the command palette and project-tree menus. - Footprints remain first-class data. Read-only footprint preview in
the Component Preview tab, Pick Footprint binding of existing
.snxfptfiles into component rows, the footprint column in the Library Browser, and the bake / library backend are all unchanged. Only the editor surface is hidden. The full editor returns in a later release by flipping the flag.
- New regression coverage pinning the footprint gate
(
opening_snxfpt_does_not_create_editable_tab_when_gated) plus a positive control proving the symbol editor still opens (opening_snxsym_still_creates_editable_tab). Fullcargo test --workspacegreen.
- Zero
kicad/KiCad/KICADsubstrings undercrates/; nokicad-parser/kicad-writerdeps or imports; no removed-API surface re-introduced;cargo-deny check licensesgreen. The cleanroom renderer was authored against Signex-only specs with no third-party EDA source in context.
The v0.11 Library & Polish milestone. Restores the full library subsystem implementation that was developed on the v0.9 branch (2026-04-26 → 2026-04-29) and parked when the Apache-clean cutover took priority. The original 169-commit chain is preserved on the v0.9-snxlib-paused-2026-04-29 and v0.10-library-cherry-pick-2026-04-29 tags; this release squashes the crates/ tree of that chain onto the post-cutover dev as a single restoration unit, with all Apache-clean invariants enforced.
The library subsystem follows the DBLib model specified in the v0.9 plan series (docs/internal/docs/v0.9-library-plan.md → v0.9-library-refactor-plan.md → v0.9-refactor-2-plan.md): components live as rows in TSV tables (tables/<category>.tsv) rather than per-component files; symbols, footprints, and sim models stay as standalone editable primitive files (.snxsym, .snxfpt, .snxsim) addressed by UUID; component rows reference primitives by (library_id, uuid) tuples. The same column schema serialises to TSV (LocalGit) or JSONB rows (Database) — one wire format, two backends.
- Component model —
ComponentRowcarriesrow_id(stable Uuid v7),internal_pn,class,datasheet,state(lifecycle),symbol_ref/footprint_ref/sim_ref(PrimitiveRef { library_id, uuid }),pin_map_overrides,primary_mpn+alternates,supply(distributor listings),parameters(template-validatedParamMap), and PLM-reserved inert fields for forward compatibility with Signex 365. - Lifecycle states —
Draft / InReview / Released / Deprecated / Obsoleteper the LIBRARY_PLAN §4 contract; placement gating +statefield changes recorded in git history (LocalGit) orupdated_at(Database). LibraryAdaptertrait — object-safe trait covering table CRUD (list_tables,read_table,iter_rows), row CRUD (read_row,read_row_by_pn,insert_row,update_row,delete_row), and primitive CRUD (get_symbol,save_symbol,list_symbols, etc.).LocalGitAdapter—*.snxlib/directory format withlibrary.toml,tables/<category>.tsv,symbols/<uuid>.snxsym,footprints/<uuid>.snxfpt,sims/<uuid>.snxsim,step/<sha256>.step. Every write commits via libgit2 with a supplied message; reads stream from the on-disk TSV.DatabaseAdapter— genericcomponent_rows (library_id, table_name, row_id, payload jsonb)schema; same column shape as the LocalGit TSV, JSONB payload preserves struct fidelity. Sync detection viacontent_hash.DistributorAdaptertrait with DigiKey (OAuth2 + PKCE), Mouser (API key), LCSC (anonymous), JLCPCB (anonymous) implementations. Per-provider 24h cache; OS-keyring storage for user-supplied keys (macOS Keychain, Windows Credential Manager, libsecret on Linux). Settings → Library → Distributor APIs UI for connecting providers + ordering preference.- AI-stub from datasheet — heuristic table extraction (PDF → text → pin-name guess) gated behind
pdf-extract. Handed back as aSymbolPinPreviewthe user reviews before committing. - Where-Used reverse index —
WhereUsedIndex::primitive_to_rowsrebuilt fromiter_rows(); click-to-jump from the Component Preview tab. - Tantivy search index — full-text index over
internal_pn / mpn / manufacturer / description / parameters; rebuilt on commit. - Diff API —
RowDiffwith per-column-group flags drives lifecycle auto-bump heuristic. - Manifest schema —
library.tomlcarries[[tables]]config (category → table name + class allowlist),[users](per-email role table),[workflow](review_required, reviewers_required, auto_lifecycle_promote).
- REST API —
GET /tables,GET /tables/:name,POST /tables/:name/rows,GET /tables/:name/rows/:row_id,PUT /tables/:name/rows/:row_id,DELETE /tables/:name/rows/:row_id. Bearer-token gated. - Lock service — advisory locks per (table, row_id, field-set) with idle TTL + WS notification on release.
- Migrations — sqlx-managed schema;
0001_initial.sqlthrough0005_tabular_components.sqlcovering both Postgres and SQLite via the same column DDL. - Lifecycle transitions with optional review workflow (per-library setting):
state = Draft→InReview→Released(with reviewer approval), or directDraft→Releasedwhenreview_required = false.
- SCH Library editor — opens a
.snxsymas a main-window tab (TabKind::SymbolEditor(PathBuf)). Multi-symbol container, per-pin Properties panel (name / number / direction / shape / position / length), drawing tools (Rectangle / Line / Circle), per-graphic Properties surface, drag-to-resize, multi-part component support viaSymbolPin.part_number,signex_widgets::active_barmigration. Save uses the v0.9.1 borrow-based pattern. - Footprint editor —
.snxfptopens asTabKind::FootprintEditor(PathBuf). Pad placement canvas, Body3D pane with STEP attach + 3D preview, layer toolbar, courtyard/silk/fab/paste-mask layers. - Library Browser tab — table on the left with clickable column headers (numeric-aware sort), Rev column showing the bound primitive's
version + releasedindicator, side preview pane on the right rendering the bound symbol + footprint viasignex-render. Substring filter across name / value / footprint / description. - Component Preview tab — 5 read-only tabs (Preview / Parameters / Supply / Datasheet / Simulation) per the v0.9-refactor-2 plan §11. Right-click the symbol render → "Open Symbol Editor" opens the standalone primitive tab.
- Library left-dock panel — flat list of mounted libraries with single-click
[Open]button. Filter input narrows the visible library list. Inline category-tree-with-row-grid is intentionally not in this panel — the canonical surface is the Library Browser tab (real libraries have thousands of components). - Components panel — Project / Installed / Global mount sources; renders the active Library Browser tab's row set as a placement palette.
- New Component modal — picks library + table + class, mints sentinel UUIDs for symbol/footprint, writes the new row + primitive files atomically. Inline cell editing in the Library Browser grid; Edit Component Details modal on row double-click.
- Pick Symbol / Pick Footprint picker — modal scoped to currently-mounted libraries; binds an existing primitive into a row's
symbol_ref/footprint_ref. Never auto-mints empty primitive files; sentinel-nilUUIDs flow through until the user picks. - Filesystem auto-mount on picker miss — when the picker can't find a binding in mounted libraries, scans the active project tree for
.snxsym/.snxfptfiles and offers them as auto-mount candidates. Selecting one mounts its parent library transparently before binding. - Library Updates dialog — detects primitive version drift across mounted libraries, applies updates row-by-row.
- Cascade engine — primitive saves bump bound rows so the Library Browser's Rev column refreshes without an explicit re-scan.
- History pane scaffold —
LibraryAdapter::historyAPI for surfacing per-row commit / migration history. - Recovery dialogs — "missing snxlib", "git directory missing", "binding broken" prompts that route the user back to a working state.
- Save-As flow for new symbols / footprints / libraries.
- Distributor-API settings UI — Settings → Library → Distributor APIs renders the connect / test / order-preference grid.
- New deps in
Cargo.toml:chrono,sha2,git2(vendored libgit2),keyring,tantivy,oauth2,reqwest,axum,tokio,sqlx,pdf-extract,tower-http,tracing,tracing-subscriber. All Apache-2.0 / MIT permissive —cargo-denyclean. CDLA-Permissive-2.0added to thedeny.tomlallowlist forwebpki-roots(transitive viareqwest+sqlx)..gitattributesadds binary patterns for.pdf,.step,.wrl,.png.
- Command palette (UX_IMPROVEMENTS_OVER_ALTIUM §4.1) —
Ctrl+Shift+Pfocuses the chrome-strip search bar and opens a fuzzy-match dropdown over every menu action, every panel-open command, every placed designator, and every project file. Sublime-text-style scoring (word-boundary + contiguous + literal-substring bonuses, length penalty); arrow keys navigate, Enter executes, Esc / click-outside dismisses. Caps the result list at 10 with a "More results — refine query" footer. Replaces the old static placeholder. Export PDF stays reachable via File ▸ Export ▸ PDF… - Hover tooltip on placed symbols (UX_IMPROVEMENTS_OVER_ALTIUM §3.4) — dwelling the cursor on a placed schematic symbol for 250 ms now opens a small floating card showing designator + value + footprint + library id. Card tracks the cursor with a 16 px bottom-right offset (auto-flips at the right/bottom edges); vanishes when the cursor leaves the symbol. Click-through is preserved — the tooltip never blocks the next click. Symbol-only by design; wires/labels carry no library metadata worth surfacing.
- Persistent search across reopens (UX_IMPROVEMENTS_OVER_ALTIUM §1.1) — the Components-panel filter and the Library Browser tab's search query are now written through to the prefs file on every keystroke and rehydrated on the next launch. Browser queries are scoped per
.snxlibpath so two open libraries don't share state. The primitive picker stays session-only by design (pickers exist to find new primitives — preserved query feels stale). - ERC violations grow a Quick Fix chip (UX_IMPROVEMENTS_OVER_ALTIUM §4.4) — every ERC diagnostic row in the Messages panel now carries a small chip next to the message.
Unused pinviolations get anAdd No-Connectchip that places aNoConnectmarker at the offending pin and re-runs ERC so the row disappears immediately; every other rule's chip is aShow on Canvasalias for the row click that gives the user a one-click target even when the row's text is long. Click on the row body still does the default zoom-and-select.
- Per-item tracking-scope picker on Enable Version Control — the modal opened from the project root context menu now shows a checkbox list of each
.snxsch/.snxpcb/.snxlibin the project. Unchecked rows are written to a generated.gitignoreat confirm time so users can scope the initial commit without manual editing. Library API (signex_library::enable_project_version_control) takes the gitignore body asOption<&str>and writes both the gitignore and.gitattributesatomically alongsidegit init, with rollback on failure — disk state never lands half-applied. - Library-level Enable Version Control — right-click an existing plain-files
.snxlibnode →Enable Version Control...opens the same modal scoped to the library directory. Surfaceslibrary.toml,components.tsv, and anyclasses//symbols//footprints//sims//models/subdirectories that exist on disk as separately tickable rows.TrackItemshape refactored to{ absolute, relative, label, is_directory, tracked }so library-scope items plug into the same picker without enum bloat. Confirm branches onVersionControlScope { Project, Library }so the post-init refresh updates the right tree node. - Per-file Git history right-dock panel — new
PanelKind::Historyfollows the active tab and renders the file's last 50 commits viasignex_widgets::history_pane. Wires on a newsignex_library::project_file_history(project_dir, rel_path)helper that walks anygit2::Repository(not just library-rooted ones). Async-loaded with a generation counter to drop stale results on tab switch. States: not-in-git / no-commits / dirty-only / normal. Working-tree pseudo-card on top when the active path is indirty_paths. - Per-class filter in Library Browser sidebar — clicking a class row in the master-detail layout's left sidebar filters the right-side component grid to rows whose
classfield matches. Clicking the active class clears the filter;LibraryBrowserState.class_filter: Option<String>applied alongsidelifecycle_filter. Active class row renders with the same accent-tint background as the table sidebar's selected row for visual feedback.
- Hardware Requirements section in
README.md(addresses #63) — Vulkan 1.1 / DirectX 12 / Metal floor (~2014+ GPU). Older GPUs that fall back to legacy OpenGL may render incorrectly. docs/RENDERING_RULES.md(new public doc) — Apache-2.0 prose describing label rendering, field rotation/justify rules, and IEEE-Std-91 pin shape decorators. Sourced fromcrates/signex-types/(Signex's own.snxschformat), Altium parity goals, and public industry standards. Source comments insignex-renderreference this spec instead of citing third-party EDA tooling.docs/audit/comments-scrub-2026-05-01.md(new audit doc) — records the 2026-05-01 source comment scrub that removed residual KiCad C++ class name references fromsignex-renderandsignex-engineandsignex-output/pdfeven after the v0.10.0 "Standard" rename. Eight comment lines across seven files reworded to neutral descriptions; algorithms unchanged.docs/internal/CLEANROOM_REWRITE_PLAN.md(private submodule) — plan for the v0.12 cleanroom milestone: deletion scope, working rules for the fresh agent session, six rewrite phases, CI guard extensions.
no-kicad-cpp-class-namesjob (new) forbidsSCH_PAINTER,SCH_FIELD::,SCH_LABEL,SCH_PIN,SCH_SYMBOL,LIB_SYMBOL::,LIB_PIN::,sch_painter.cpp/sch_symbol.cpp/sch_label.cpp/sch_pin.cpp/lib_symbol.cpp, andeeschema/anywhere incrates/.no-derivation-attribution-markersjob (new) forbidsDeepWiki,KiCad mirror source,extracted from KiCad,based on KiCad sourceanywhere in the repo (excluding the audit trail anddocs/LICENSING.md).- Total License Guard jobs: 6 → 8.
LibraryCreateOptionsStateand the Enable Version Control modal hoist theintroparagraphformat!out of the view-body function — built once at modal-open time and stored on the state, no per-frame allocation.
PinElectricalType→PinDirectionin the newsignex-librarycrate to satisfy the License Guard'sno-removed-kicad-apistrict job. Variant set unchanged. Note: this is a different enum fromsignex_types::schematic::PinDirection(which has Signex-original variants); the two coexist as path-qualifiedsignex_library::PinDirectionvssignex_types::schematic::PinDirection. Consolidating them is a follow-up refactor.crates/signex-types/src/library.rs— the v0.10.0 thinLibrary/LibraryComponenttypes are removed. Library Browser tab content now reads through thesignex-libraryadapter trait.assets/samples/library/resistors-standard.snxlib— removed; the obsolete v0.10.0 sample no longer fits the DBLib data model.
- Three internal-docs plans landed in
docs/internal/docs/via the private subrepo:v0.9-library-plan.md(foundation),v0.9-library-refactor-plan.md(primitive split / DBLib shape),v0.9-refactor-2-plan.md(table-row + UI layout spec). The olderLIBRARY_PLAN.mddesign-intent doc is retired in favour of the concrete implementation plans. docs/audit/history-rewrite-2026-04-29.md— records the rationale, scope, and audit trail of the 2026-04-29 KiCad-name scrub history rewrite. Cosmetic, not a license remediation; full pre-rewrite chain preserved in the maintainer's backup repo.docs/internal/docs/issue-62-execution-plan.mdandissue-62-licensing-remediation.md— strategy docs behind the v0.9.0 Apache-clean cutover, pulled into the canonical internal docs home.
Every commit on the original 169-commit chain is preserved both locally and on origin under:
refs/tags/v0.9-snxlib-paused-2026-04-29(b9eac1f3) — library WIP as paused for the Apache-clean cutover.refs/tags/v0.10-library-cherry-pick-2026-04-29(012264fb) — fully reconciled post-cutover state, parent of this restore.refs/tags/v0.11-pre-library-restore-2026-04-29(e8b8e8f5) — branch tip before the squash, preserves the Phase A polish (filter + preview pane scaffolding) that was superseded by the orphan's richer Library Browser.
The squash here is a tree-only restoration; full per-commit authorship and history is reachable via those tags.
- Zero
kicad/KiCad/KICADsubstrings undercrates/(License Guard'sno-kicad-shaped-symbolsstrict job: PASS). - No
kicad-parser/kicad-writerCargo deps or imports. - No removed-API surface re-introduced (
PinElectricalType, numericLayerIdconstants,parse_markup,kicad_auto_net_name_from_pins,find_kicad_symbols_dir, …). cargo-deny check licensesgreen — every transitive dep is permissive.- Every PR description carries the self-declaration block (Source basis / LLM-assisted / KiCad source consulted).
First slice of the v0.10 Library & Polish milestone — the Library Browser tab scaffold. Double-clicking a .snxlib file in the project tree now opens a dedicated tab that lists the components contained in the library package; the surface is intentionally read-only this release. v0.10.1 adds the side-by-side symbol preview pane on row click; v0.10.2 adds the filter / search bar above the table.
The library subsystem paused on 2026-04-29 alongside the v0.9.0 Apache-clean cutover (preserved at the v0.9-snxlib-paused-2026-04-29 tag) is being re-landed file-by-file on top of the Apache-clean foundations rather than wholesale-rebased — every contributing change is verified against the issue #62 invariants before it ships.
signex-types::library::LibraryandLibraryComponent— in-memory representation of a.snxlibpackage.LibraryComponentcarries a sentinel-nilsymbol_uuid/footprint_uuidfor unbound rows; v0.10.8 wires the Pick Symbol/Footprint flow that fills them in.signex-types::format::SnxLibrary— TOML+TSV envelope for.snxlibfiles.parse(&str),write_string(), andwrite_string_borrowed(&str, &Library)mirror the v0.9.1 borrow-based pattern fromSnxSchematic/SnxPcb, so v0.10.6's async-save plumbing drops in unchanged.signex-types::format::LibraryComponentRow— TSV adapter row implementingSnxTablewith columnsuuid name value footprint description symbol_uuid footprint_uuid.signex-app::TabDocument::Library(Library)variant — Library Browser tabs cache the parsed library on the tab. Newas_library()accessor; the existingas_pcb()is unchanged.signex-appopen path —open_document_pathand the project-tree handler both route.snxlibto a newopen_library_tab(path, title, library)mirroringopen_pcb_tab.signex-app::view::view_library_browser— read-only Library Browser tab body. Header strip (library name + component count + optional description), 4-column scrollable table (Name, Value, Footprint, Description), and an empty-state placeholder when the library has zero components.assets/samples/library/resistors-standard.snxlib— 3-component sample library shipped with the source tree for the smoke test.
- Project tree click semantics — single click highlights, double click opens. Previously a single click on a leaf both highlighted and opened the file, which was easy to trigger accidentally while navigating. Now a single click only highlights the row (
panel_ctx.selected_tree_path); a second click on the same row within 500 ms opens the file. The icon-gate (only schematic / PCB /.snxprj/.snxlib/.snxsym/.snxfpt/.snxsimleaves open) is unchanged. Right-click → Open in the context menu still opens immediately, bypassing the double-click latch. Folder rows still toggle expand/collapse on a single click. Implementation lives insignex-app::handlers::dock::project_navigation::handle_dock_project_navigation_panel_messageand routes through a newopen_tree_path_if_documenthelper shared with the right-click menu path.
In response to ongoing discussion on issue #62, v0.10.0 also lands a residual-polish pass that removes vestigial KiCad-shaped names from the codebase. None of these changes alter user-visible functionality, but they reduce the surface that reads as "Signex was once derived from KiCad" — the substantive change was already delivered in v0.9.0; this is the cosmetic follow-through.
- Style enum variant renames.
MultisheetStyle::KiCad/LabelStyle::KiCad/PowerPortStyle::KiCad→::Standardfor all three. The::Altiumvariants stay (Altium is the project's stated design reference and isn't a GPL exposure). On-disk preference strings remain"kicad"/"altium"for backward compatibility with existingprefs.jsonfiles; the user-facing dropdown labels also stay "KiCad" / "Altium" so users coming from those tools recognise the modes. - Legacy KiCad symbol-library scanner removed.
helpers::find_kicad_symbols_dir(which walked/usr/share/kicad/symbols,C:\Program Files\KiCad\…, etc.) andhelpers::list_kicad_librariesdeleted. The associatedDocumentState::kicad_lib_dirfield,PanelContext::kicad_librariesfield, and the dock-panellibrary_browser.rshandler are gone. The Components panel's library dropdown is replaced by an inline placeholder until the v0.10.x.snxlibplumbing repopulates it. The scanner had been a no-op since v0.9.0 (the load handler logged "convert with companion tool" and skipped); removing it cleans up code that pointed at KiCad install layouts without serving a real flow. - Direct-open KiCad-extension dispatch arm removed.
open_document_pathno longer matches"kicad_pro" | "kicad_sch" | "kicad_pcb"; the same arm in the project-tree double-click handler is also gone. Opening a.kicad_*file now falls through to a generic "unsupported file type" error. The migration story for KiCad users is unchanged — runsignex-kicad-importfirst; the README anddocs/LICENSING.mdcontinue to describe the flow. docs/LICENSING.mdstrengthened. New "LLM context discipline" section documenting that post-cutover development uses LLM-assisted workflows where KiCad source code is never placed in agent context, prompts, retrieval indexes, or reference material. Versions table updated through v0.10.0.- License Guard CI tightened. Three new jobs added to
.github/workflows/license-guard.yml: forbid re-introduction of the v0.10.0-renamed names (MultisheetStyle::KiCad,find_kicad_symbols_dir, etc.); forbid KiCad-numbered layer-id constants (F_CU = 0,B_CU = 31, …); forbid theNet-(<r>-Pad<p>)auto-net-name format string.
signex_types::format::tests::snxlibrary_round_trip_preserves_components— locks parser/writer round-trip parity.signex_types::format::tests::snxlibrary_borrow_matches_owned— owned/borrowed serialise parity.signex_types::format::tests::snxlibrary_rejects_unknown_version—UnsupportedVersionerror path.signex_types::format::tests::snxlibrary_parses_empty_components_block— empty-library round-trip.signex_types::format::tests::shipped_sample_library_parses— guards the shipped sample against parser drift.
- No
use kicad_parser/use kicad_writerincrates/. - No
kicad-parser/kicad-writerCargo.toml deps. - License Guard 4 jobs and
cargo-denycontinue to gate every PR.
The async save + borrow-based serialise patch deferred from v0.9.0. Schematic saves were already imperceptible; this release targets the huge-PCB Ctrl+S stutter (~1–2 s on ~500 K-track boards) by moving the disk write off the UI thread and skipping the full-document clone that the previous serialise required.
signex-types::format::SnxSchematic::write_string_borrowed(&str, &SchematicSheet)and the matchingSnxPcb::write_string_borrowed(&str, &PcbBoard)— borrow-based serialise. The ownedwrite_string()methods now delegate to these, so byte-for-byte output is unchanged. Skips the ~50–100 msself.sheet.clone()/self.board.clone()that the engine previously paid before each serialise.signex-engine::Engine::serialize_for_save(&self) -> Result<Vec<u8>, EngineError>— pure, side-effect-free serialise using the borrow path. Cheap to call repeatedly; no path mutation.signex-engine::Engine::write_to_file(path, bytes)— stateless disk write half of the async-save pair. Pair withserialize_for_saveto run the write off the UI thread.signex-engine::Engine::record_saved_path(path)— set the engine's path after an async save resolves.signex-appsave handler —Ctrl+Sand File → Save now serialise on the UI thread (cheap with the borrow-based path) and dispatch the disk write viaiced::Task::perform. iced's tokio runtime runs the blockingstd::fs::writeon a worker thread, so the UI stays responsive even on huge boards.- New
Message::SaveFileFinished(PathBuf, Result<(), String>)completion arm. - Status bar shows a small "Saving…" pill for the duration of the off-thread write; transient save errors surface as a 3-second pill before fading.
signex_types::format::tests::schematic_borrow_matches_owned_serialise— locks owned/borrowed parity forSnxSchematic.signex_types::format::tests::pcb_borrow_matches_owned_serialise— same, forSnxPcb.signex_engine::tests::serialize_for_save_returns_parseable_bytes— serialise + reparse round-trip.signex_engine::tests::write_to_file_writes_serialised_bytes— disk write + reparse round-trip via tempfile.
The Apache-clean cutover release. Resolves issue #62 raised by Seth Hillbrand of the KiCad project flagging that several Signex crates derived from KiCad's GPL-3.0 source were shipping under Apache-2.0. The main signex repository is now Apache-2.0 clean and contains no KiCad-derived code; KiCad I/O moves to the optional signex-kicad-import companion tool (GPL-3.0-or-later), shipped independently.
The library subsystem (Library Browser, SCH Library editor, Component Editor) that was in flight on feature/v0.9-snxlib-as-file is preserved at the v0.9-snxlib-paused-2026-04-29 tag and ships as v0.10.0 on top of the Apache-clean foundations from this release.
.snxschschematic format — TOML manifest (format = "snxsch/1") wrapping theSchematicSheetpayload via 4 adapter row tables ([sheets.components],[sheets.wires],[sheets.junctions],[sheets.labels]) plus an[extras.*]TOML block for fields that don't fit a flat row schema. Same format family as.snxlib/.snxsym/.snxfpt..snxpcbPCB format — same shape with adapter rows for[footprints],[pads],[tracks],[vias], plus[stackup]/[[zones]]in regular TOML for hierarchical data.- Format chosen for line-diff-friendly git workflows, ~5× smaller than the equivalent JSON, single file per design, and zero S-expression-shaped grammar that could regress KiCad-derivation exposure.
- Round-trip preserves every field on
SchematicSheet/PcbBoard.
PinElectricalType(12-variant, KiCad-shaped) →PinDirection(14 variants — adds Signex-originalGroundReference,Differential,Clock; collapsesFree+UnspecifiedintoUnclassified; renamesOpenCollector/OpenEmitter→OpenDrainLow/OpenDrainHigh). Design rationale incrates/signex-types/docs/pin-design.md.PinShape(9-variant) →PinShapeStyle(7 variants — drops per-direction-low modifiers since polarity is now onPinDirection; addsHysteresisInput/HysteresisOutput/Schmitt).- KiCad-numbered
LayerId(u8)constants (F_CU=0,B_CU=31, …) →SignexLayersemantic enum +LayerKindcategories +altium_label()per the Altium-flavoured Signex UI naming. - KiCad markup parser (
~{X}/^{X}/_{X}curly-brace syntax) →parse_signex_markupusing a Markdown subset:**bold**,*italic*,~~strike~~,^superscript^,~subscript~,_~overbar~_(Signex extension for active-low signal naming),[label](url),\Xescape. kicad_auto_net_name_from_pins(Net-(<r>-Pad<p>)format string) →auto_net_namereturningunnamed-<sheet>:<ref>:<pin>.
crates/kicad-parser/(3,938 LOC) — moved to the GPL-3.0 companion repo.crates/kicad-writer/(2,274 LOC) — moved to the GPL-3.0 companion repo.crates/signex-output/src/netlist/kicad_sexpr.rs(336 LOC) — KiCad netlist exporter; moved to the companion or a futuresignex-kicad-exportsibling.
- New separate repository at https://github.com/alplabai/signex-kicad-import.
- Houses the relocated
kicad-parser+kicad-writercrates plus a CLI binarysignex-kicad-import. - One-way conversion:
.kicad_sch/.kicad_pcb/.kicad_pro→.snxsch/.snxpcb/.snxprj. Originals remain intact. - Distributed independently — Apache consumers of Signex Community see no GPL aggregation in their build closure.
Async save and borrow-based serialise were planned for v0.9.0 but
are deferred to v0.9.1 to bound the v0.9.0 release scope. The
current Engine::save_as runs synchronously on the calling thread
with a full document-clone before serialisation; on huge PCBs (~500K
tracks) this blocks the UI for ~1–2 s on Ctrl+S. Tracked as a v0.9.1
follow-up:
Engine::save_as_asyncreturning aniced::Taskthat serialises + writes off the UI thread.- Borrow-based
SnxSchematic::write_string_borrowed(&SchematicSheet)to skip the document-clone (~50–100 ms saving on huge PCBs).
Schematics save instantly today; the perf work is targeted at the high-end PCB case.
.github/workflows/license-guard.yml— fails any push or PR that re-introduceskicad-parser/kicad-writerimports or removed KiCad-shaped types..github/workflows/pr-license-declaration.yml— fails PRs whose description is missing the contributor self-declaration block or whose author markedKiCad source consulted: yes.cargo-denylicense job in.github/workflows/ci.yml— rejects GPL / AGPL / LGPL / unlicensed transitive dependencies.deny.toml— Apache-compatible permissive allowlist.
docs/LICENSING.md— canonical licensing rationale + audit-trail pointers + contributor guidance.docs/audit/kicad-derivation.md— file-by-file audit of every removed item plus the residual-mention catalog.docs/audit/contributors-2026-04-29.md— contributor consent record (audit trail).docs/audit/third-party-kicad-parsers.md— survey of clean-room third-party Rust KiCad parsers.docs/audit/release-notes-remediation-v07-v08.md— text to apply manually to v0.7.0 / v0.7.1 / v0.8.0 GitHub Release bodies (those releases shipped Apache-2.0 with KiCad-derived code in error; flagged superseded).docs/audit/communication-drafts.md— drafts of the issue #62 reply, signex.dev hero copy, GitHub Discussions sticky, Discord post.CONTRIBUTING.md— added License compliance section + PR self-declaration block.crates/signex-types/docs/pin-design.md— rationale for the curatedPinDirection/PinShapeStylevariant sets.
.kicad_sch/.kicad_pcb/.kicad_profiles no longer open directly in Signex. Users with existing KiCad projects install the signex-kicad-import companion tool, run it once against their.kicad_pro, and open the resulting.snxprjfrom then on.signex_types::schematic::PinElectricalTypeandPinShapeare gone — downstream code usesPinDirection/PinShapeStyle.signex_types::layer::{F_CU, B_CU, F_SILKS, …}constants are gone — downstream code usesSignexLayer::*variants.signex_types::markup::parse_markupandkicad_auto_net_name_from_pinsare gone — downstream code usesparse_signex_markupandauto_net_name.crates/kicad-parserandcrates/kicad-writerare gone from the workspace.
The v0.7.0 / v0.7.1 / v0.8.0 release notes have been edited to flag those releases as superseded with the licensing notice. Binaries remain available for historical use; please prefer v0.9.0 (or later) for new installations.
The output-and-polish release. Adds the full PDF / BOM / netlist export pipeline, multi-project workspaces, Altium-style dirty tracking, a chrome refactor with the new TabPill widget, hierarchical-sheet rendering parity, and KiCad-parity field autoplace. Every v0.8.x sub-feature ships under this one tag.
- PDF export — tabbed Export modal (file picker / settings / pan-drag preview), bookmarks per sheet, theme palette aware, DPI hookup, physical-structure tokens
- BOM — Altium-spec preview modal with column picker, variant picker, sort / drag / scroll, options applied on export, format-layer export (CSV / HTML / XLSX-ready)
- Netlist — date + path polish, KiCad-format export, output engine validation
- Unified PDF preview modal — File ▸ Export PDF and File ▸ Print Preview both open the same overlay; legacy
view_pdf_options_dialog+ 12ExportPdfSet*/ExportPdfDialog*variants pruned
- Multiple
.snxprjprojects open side-by-side in the same window TabInfocarriesOption<ProjectId>;active_projectscoped to focused tab- Accent-tinted active project root + per-project Close menu action
- Per-tree-path semantics for project tree actions (right-click on project B with project A active still operates on B)
- Phase 2.5 cleanup — legacy single-project fields removed from
DocumentState(#54, #55, #56)
- Altium-style
dirty_pathsmodel — closing a tab never prompts; engines park while dirty - Project-close prompts with a Save All / Discard All / Cancel modal listing every dirty file
- Tab bar right-click menu replaces the inline close / undock buttons
- Child-sheet pins rendered as Altium-style ports (no protruding stubs), inward direction
- Pin labels rotate vertical on top / bottom edges for parity
- Per-sheet stroke / fill colours round-trip and editable from the Properties panel
[[multisheet]]Style preference (with sheet-alpha round-trip fix)- Child-sheet name / filename rendered outside the box per multisheet style
- Inline preset palette under the colour row before opening the picker
- Altium-green default sheet palette
- Autoplace fields on rotate / mirror — body-bbox classification, full text-height clearance, anchored to selection bbox including pins, two-text-height autoplace clearance, body-edge stack alignment
- Reference and Value rotate independently; rotate / mirror compose with symbol orientation
- Property
justifymirrored under rotation fold and mirror flags - KiCad
GetDrawRotationtoggle parity; symbol field rotation treated as absolute screen angle - Re-autoplace marked fields on load to repair legacy rotations
- Property
justifyparsing defaults to Center per KiCad spec - Pin numbers rotate along the pin axis for vertical pins
- Single em-size used for schematic text (parity with KiCad)
- New
TabPillcustom widget — 3-sided borders sharing L / R edges between adjacent tabs, permanent strip baseline, theme-border instead of pure black, drag accent follows theme accent - Modal close button unified across all 11 modals; chrome icon sizes bumped; thicker panel strokes; modal alignment so borders trace rounded corners
- Inactive tab fill is now visible (was theme-bg, blending in)
- New
chrome-catalogcrate + UI iteration workflow doc - Taller top-chrome search bar (24 → 28); narrow Justification labels in Properties
- Adaptive multi-level grid that scales smoothly with zoom
- Schematic editor grid style preference
kicad-parser/kicad-writerround-trip per-sheet stroke / fill colours and respect label style for defaultssignex-types::Labelgainsjustify_vto match renderer + parser contract- Engine command surface expanded with multi-project routing
- #54 multi-project workspace
- #55 multi-project + chrome polish + unified PDF preview
- #56 phase 2.5 cleanup of legacy single-project fields
Full changelog · Release artifacts
Patch release addressing a macOS launch failure on Apple Silicon.
- macOS (Apple Silicon) cannot launch the shipped
.app(#49). The DMG-packaged bundle was unsigned; arm64 macOS refuses to execute any binary without at least an ad-hoc signature, so users on M-series Macs saw "Signex is damaged and can't be opened" / "cannot be verified" immediately after dragging the app to Applications. The installer script now ad-hoc signs the bundle (codesign --force --deep --sign -) as part of DMG assembly. This is the minimum viable shipping state for arm64 until a Developer ID certificate and notarisation credentials are wired into CI.
- The DMG still carries the downloaded-from-internet quarantine flag,
so first-launch users will see a "cannot be verified" Gatekeeper
prompt. Bypass it with right-click → Open on the app icon the
first time, or run
xattr -dr com.apple.quarantine /Applications/Signex.appin Terminal. Subsequent launches work without prompts.
The schematic-phase release. Adds ERC & validation, project-wide annotation, real multi-window architecture via iced::daemon, per-window engine/canvas, borderless chrome, and a full Signex brand rollout. Every v0.7.x sub-feature ships under this one tag.
- New
signex-erccrate with 11 rule kinds (run()single-sheet,run_with_project()cross-sheet) - Project-wide ERC across open, cached, and unopened sheets
- Cross-sheet BadHierSheetPin: parent pins ↔ child hier-labels, both directions
- ERC markers rendered as overlays — zero KiCad schema drift
- Messages panel with E/W/I severity pips and per-sheet cached results
- Altium-style ERC dialog + Preferences panel (per-rule Error / Warning / Info / Off grid)
ErcContextabstraction + rule metadata infrastructure- Shortcuts:
F8Run ERC,F9AutoFocus,Alt+AAnnotate,Shift+Alt+AReset & Renumber
- Project-wide change list parsed from every sheet in the project
- Altium two-column Annotate dialog with Reset All / Reset & Renumber / Reset Duplicates
- Per-symbol lock, draggable modals
- Power-port skip in both annotation and change list
- Design → Annotation submenu matching Altium's layout
- Borderless main window + OS-decorated secondary windows
- Modals, tabs, and panels all detach into real OS windows
- Per-window engine:
document_state.engines: HashMap<PathBuf, Engine>— every open tab keeps its Engine loaded so undocked tabs edit independently - Per-window canvas:
interaction_state.canvases: HashMap<window::Id, SchematicCanvas>— pan / zoom / selection / render cache per window document_state.window_active_path: HashMap<window::Id, PathBuf>— each undocked-tab window can show a different tab- Canvas events routed via
CanvasEventInWindow { window_id, event } - macOS: process exits when main window closes
- Lasso freehand select (Altium), bbox child-sheet hit, detached-field support
- Tab / panel drag-reorder with visual feedback
- Reorder picker polish — gray-X cursor while armed, Esc-cancel visible
- Unified gray-X placement cursor across every armed tool
- TAB-during-placement for Line / Rect / Circle / Arc / Polygon (width + fill pre-configured)
- Editable drawing Properties panel with live DrawingPreview canvas
- Stroke colour per drawing (round-trips in KiCad format)
- Erasable numeric
text_input(per-fieldStringbuffer) - Context-aware menus — Annotate / ERC / Save / Edit gated by
has_schematic/ selection - Net-colour pen — strict-hit snap, union-find flood, render-only overrides, undo stack
- Move / z-order engine commands —
MoveSymbolAbsolute,ReorderObjects(Front / Back / JustAbove / JustBelow) - Active Bar — BringToFront / SendToBack + BringToFrontOf / SendToBackOf pickers
- Reset Duplicate Designators (project-wide, undoable for open tabs)
- Arc (3-click) + Polygon (click-by-click) placement tools
- Double-click a sheet block → opens the child schematic
- Sheet-pin snaps to all four edges of the hierarchical sheet block
- Sheet-pin interactions + Altium label-style option
- Custom title bar: wordmark + menus + drag zone + search bar + min / max / close
- Per-monitor v2 DPI manifest (no bitmap stretching on hi-DPI)
- Roboto UI font (panels / toolbars / menus / dialogs); Iosevka stays the canvas font
- Windows 11 DWM rounded corners + drop shadow via
DwmSetWindowAttribute(silent no-op on Win10 and non-Windows) - Diagonal resize hit zones (NW / NE / SW / SE) via Stack overlay — keeps content y-origin natural
- Header logo bumped 74×24 → 96×31 for readability
- Signex brand SVGs (mark, wordmark, logo variants)
- Panton Bold wordmark regenerated from actual font (not fallback outlines)
- Tighter
signex-mark.svgviewBox: S fills ~97 % of icon canvas (was ~58 %) - Regenerated installer artifacts: Windows
.ico(multi-size), macOS.icns, Linux PNGs - Runtime window icon embedded via
iced::window::Icon signex.exeicon + DPI manifest embedded at build time viawinres- Pure-Python fallback for
build-icons.sh—tools/build_icons.py, no rsvg-convert / magick / inkscape needed
signex-engine/src/lib.rssplit into semantic moduleskicad-writermigrated fromwln!string formatting to full SExpr AST (kicad-parser/sexpr_builder→kicad-writer/sexpr_render)- Named constants for PCB magic numbers;
lib_symbolunit round-trip fix - Wire rendering chains connected segments into polylines (rounded corners)
- Font-size constants corrected, hidden refs and pin-name rotation fixed
- v0.6.1 render fixes (font scale, pin numbers, power-ref visibility)
- v0.6.2 AST sexpr pipeline
- v0.6.3 macOS runner pin (
macos-14) - v0.6.4 per-OS installers (
.exe,.dmg,.deb,.AppImage) - Node.js 24 Actions bump
expand_to_netis nowO(N)via quantisedHashSet(wasO(P²·N²))
Full changelog · Release artifacts
First cumulative release since v0.6.1. Rolls in the abandoned v0.6.2 and the CI-fix-only v0.6.3.
- Windows —
signex-setup-x86_64-0.6.4.exe/signex-setup-aarch64-0.6.4.exevia InnoSetup. Installs toProgram Files, adds Start Menu entry and optional Desktop shortcut, proper uninstaller. Portable.zipalso attached for scripted installs. - macOS —
signex-macos-aarch64-0.6.4.dmgwith a fullSignex.appbundle and/Applicationsdrag-target. Registered as the editor for.kicad_sch/.kicad_profiles. - Linux — native
.deb(with.desktopentry + MIME types) plus a portable.AppImage..tar.gzfallback also attached.
- AST-based S-expression pipeline in
kicad-parser/kicad-writerwith a property-metadata layer. More robust round-trip, less fragile than prior ad-hoc string handling. - Named constants replace the magic numbers scattered through
signex-types,kicad-parser, andkicad-writer.
aarch64-apple-darwinpinned tomacos-14so GitHub'smacos-latestqueue stalls don't take the whole release down (symptom that killed v0.6.2).
No .kicad_sch / .kicad_pcb breaking changes — opening a v0.6.1 project in v0.6.4 is a clean round-trip. If you were on v0.6.2 or v0.6.3, nothing additional to migrate — v0.6.4 is a superset.
- fix(ci): pin aarch64-apple-darwin to macos-14 (#34)
Render + KiCad round-trip fixes (font scale, pin numbers, power-ref visibility).
Full Schematic Editor — drag-move, properties editing, placement tools, iced_aw, Active Bar.