The detail block was rendered after *all* health rows, so it dangled below
the new model: row instead of the NC: row it explains — its indentation read
as broken. Build it into health_rows right after the NC row, with a tree
marker (↳ dropped (only 1 pin on the net):) and consistent nesting.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace internal jargon and uncommon words in user-facing strings (better for
non-native English readers): drop "TransformRegistry-driven" / "drives
transforms" from the connect/set-connector-type subtitles; "transform lookup"
→ "tells connect how to wire its pins"; "populate pin specs" → "fills in each
pin's role and direction"; "clear the visualization area" → "clear the console
output"; "materialised" → "added"; refresh the verify description. Regenerated
commands.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
"dropped detail:" said nothing about what those pins are. They were detached
by drop_singleton_signals because each was the lone pin on its net (nowhere to
connect → NC). Relabel to "dropped — lone pin on its net (→ NC):".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
check_bsdl_completeness(System*): for each BSDL-attached part, re-parse the
.bsd and report the device power/ground ports with no matching pin on the
netlist part (matched by port name or physical pad) — a rail the schematic
symbol is missing. One aggregated BsdlPinMissing per part; restricted to
power/ground so unused I/O balls don't create noise. Surfaced as a 7th verify
pass and in the analyze/dashboard model counts. 76 cases / 327 assertions
green; the real 8-card system reports 0 (all FPGA rails present). This closes
out P3.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The analyze screen's Issues pane now lists the model-driven checks
(check_pin_specs / check_jtag_chain / check_source_conflicts) alongside the
pin-role, net-mix and structural ones, with an "N model" count in the header;
the dashboard gains a "model:" health row. check_pin_specs/check_jtag_chain
take an optional precomputed net list, so verify, analyze and the dashboard
each compute the nets once and reuse them across checks instead of redoing the
transitive closure per check. Unit tests (75) green; verify output unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rank the spec sources (spec_source_rank: UserOverride > Bsdl > ConnectorModel
> Inferred > Imported); apply_model now refuses to overwrite a spec owned by a
higher-rank source, so one model never clobbers a more authoritative one. New
check_source_conflicts(System*) emits SourceConflict for a pin the BSDL
declares power/ground (a must-connect rail) that the netlist leaves
unconnected — a rail floated in the schematic; surfaced as a sixth `verify`
pass. Unit tests (75 cases) green; the real 8-card system reports 0 conflicts
(its rails are all connected) while the JTAG findings remain.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New PinModel interface (spec_for / layout / source) + a single apply_model(
Part*, const PinModel&) that materialises missing layout pins and sets each
pin's spec only where the model speaks (spec.source != None), so one source
never clobbers another's. ConnectorModel wraps pin_role/pin_layout;
BsdlPinModel wraps a parsed BsdlModel (indexed by port name and physical pad).
set-connector-type and screen_settype now use ConnectorModel + apply_model;
attach-bsdl and the restore re-apply keep calling apply_bsdl, now a thin
adapter over apply_model. Behaviour-preserving: unit tests (73 cases) green and
the real 8-card system re-runs identically (1517/1517 bound, same JTAG
findings). Covered by test_pin_model.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DESIGN.md: libbsdl dependency and --batch headless mode; bsdl_model/bsdl_check
in the layout; the attach-bsdl command and the `B` persist tag; PinSpec is now
BSDL-populated; verify's five passes incl. the model-driven and JTAG checks
and the new AnomalyKinds. README: libbsdl dependency, --batch usage, tutorial
link. New doc/user/tutorial.md: end-to-end batch and TUI walkthroughs (load →
tag → connect → attach-bsdl → verify, with the pin/JTAG findings explained).
Regenerated commands.md (adds attach-bsdl); index.md links the tutorial.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Real-data testing (3 BSDL-attached FPGAs in an 8-card system) showed undriven
over-fires when only one side of a net has a known direction: the driver sits
on an un-modelled part (direction Unknown). Require known == net pin count, so
"undriven" is concluded only when every pin on the net is modelled. Drops 216
false positives to 0 on the sample system while the genuine JTAG findings
remain; the unit test is unaffected (its net is fully modelled).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BootDispatch already runs --restore/--source synchronously before the TUI
starts (Source takes its headless drain branch when no screen is attached),
so the console buffer is complete by then. New --batch flag dumps that buffer
(Tui::DumpOutput) to stdout and exits without launching the TUI — enabling
scripted/CI runs and verify output capture (e.g. essim --batch --source s).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New check_jtag_chain(System*): collects TAP pins by PinSpec.function, resolves
each to its net, and flags JtagTapIncomplete (a device missing TDI/TDO/TMS/
TCK), JtagBusUnbridged (TMS or TCK not common to every TAP device), and
JtagChainBreak (dangling TDO/TDI, chain fan-out, or not a single head->tail
daisy chain). Surfaced as a pass in `verify`; AnomalyKind extended. Covered by
test_bsdl_check (healthy chain, broken chain + split bus, incomplete TAP).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New bsdl_check.{hpp,cpp}: check_pin_specs(System*) walks the nets and uses
each pin's PinSpec direction/function to flag DriveContention (>=2 push-pull
output drivers), UndrivenNet (a multi-pin net with input(s) but no driver),
and NcWired (a no-connect pin wired onto a multi-pin net). Added as a pass in
`verify`; AnomalyKind extended accordingly. Nets with no direction data are
skipped, so un-modelled parts produce no noise. Covered by test_bsdl_check.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New `attach-bsdl <module> <part> <file.bsd>` command: parse via BsdlModel,
apply_bsdl() onto the part, store the path on Part::bsdl_path, report bound/
unbound. Persist a `B\t<path>` line under the part; on restore, re-parse and
re-apply each attached model (the .bsd path is persisted, not the derived
pin specs). Round-trip covered by test_bsdl_apply.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Link libbsdl dynamically (add_subdirectory ../libbsdl, overridable via
-DBSDL_DIR). New BsdlModel wraps the C ABI and reduces a parsed .bsd to
essim's pin vocabulary; apply_bsdl() binds each port to a Pin (by name, then
by physical pad) and sets its spec: direction, function (TAP role / power /
ground / signal), pad, and source = Bsdl.
This feeds the PinSpec fields from P1, so verify's existing power/ground
placement pass now lights up for BSDL-modelled parts. Covered by
test_bsdl_apply (name + pad binding, TAP roles, linkage classification).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DESIGN.md: Pin now carries a PinSpec (function/direction/pad/source);
expected_signal_type() is a derived accessor; pin_role() returns a PinSpec.
README.md: dedicated Dependencies section with libzip/pugixml install
commands for Debian/Ubuntu, Arch and Fedora.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce PinSpec (function/direction/pad/source) as the "expected" half of
pin verification, and make Pin::expected_signal_type() a derived accessor over
spec.function. pin_role() now returns a PinSpec; the connector layout (and,
later, BSDL) feed the same structure.
Pure refactor, behaviour-preserving: vpx_3u_role is still a stub, so every pin
maps to Other exactly as before. The new `pad` field will carry the BSDL
physical pin; direction/function will unlock contention/undriven/NC checks.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ODS sheets now carry a per-connection meta block (Connection / Transform
/ Left / Right) above the data; the header row anchors the freeze, the
auto-filter range, and the zebra striping. CSV stays a single flat
15-column table whose names match the ODS headers exactly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ODS writer:
- Header row styled bold on a grey background; data rows alternate
light-grey / white (zebra). Auto-filter buttons enabled per sheet
via `<table:database-range table:display-filter-buttons="true">`
— sheet names are now single-quoted in the target-range-address so
LibreOffice actually parses ranges that contain spaces.
- First row of every sheet frozen via `settings.xml` (view setting).
The settings layout follows LibreOffice's own writer exactly:
`xmlns:ooo` + `xmlns:xlink` declared on the root, `ActiveTable` and
view-level zoom/grid/headers placed *after* the Tables map (the
order LibreOffice expects to read back). HorizontalSplitMode = 0,
VerticalSplitMode = 2 → only the first row is frozen, the first
column scrolls normally. ActiveSplitRange = 2 (bottom-left pane).
`settings.xml` registered in `META-INF/manifest.xml`.
Reusable Yes/No modal:
- New `screen_confirm.cpp` exposing `Tui::ShowConfirm(msg, on_yes)`.
Centred `borderRounded` popup, `No` first (safer default), Enter
confirms the focused button, Esc cancels (treated as No).
- Stacked into the Modal chain in `Run()` (between file-dialog and
error). The outer `CatchEvent` cedes events when it's open.
File dialog:
- Picks `OK` button focus reliably — previously the focus indices in
the Renderer were remapped against the Container::Vertical child
indices, so the OK label only flagged "focused" while the actual
focused child was the filename input.
- OK button uses a custom `ButtonOption::transform` that renders the
label transparent when idle, inverted when focused. No more
cyan-fill, no more double-inversion via FocusLabel.
- After confirmation, if the picked path already exists, pops a
`ShowConfirm("File … already exists. Overwrite?")` before invoking
the caller's action.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
LibreOffice rejected the generated `.ods` with `Format error at 2,39
in content.xml`. Root cause: `pugi::format_no_declaration` suppresses
only the *implicit* declaration auto-added at save time — the explicit
`node_declaration` I had appended to the document still got serialised,
on top of a manual `<?xml…?>` string prepend in the output. Two
declarations back-to-back, invalid XML.
Fix: let pugixml emit the explicit declaration node, drop the manual
prepend.
Also harden the sheet-name sanitiser in the export action: ODS / Excel
also forbid `< > &` in raw cell or table names, so the default
connection name `bp/J20 <-> payload1/P0` made content.xml entity-
escape `<` to `<`, which a few viewers handle but Excel rejects.
Clip to 31 chars too (Excel's hard limit) so multi-name connections
don't blow up the open.
Verified by `soffice --headless --convert-to csv` round-tripping the
output without errors.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two focused, behaviour-preserving moves:
1. `OpenSignalTypeDialog` + `ApplySignalTypeChoice` moved from
`shell.cpp` to `screen_sigtype_modal.cpp` so the popup owns all of
its logic instead of having its open/apply functions live in the
shell file.
2. The `export` command extracted from `commands.cpp` to a new
`commands_export.cpp` under a `Tui::RegisterExportCommands()`
member. `RegisterCommands()` calls it at the end. File-local
helpers (`csv_quote`, `pin_side`) move alongside in an anonymous
namespace.
Establishes the pattern for future per-group splits: declare a
`Register<X>Commands()` member, define it in its own file, call it
from the orchestrator. Other groups stay in `commands.cpp` for now —
nothing else has grown large enough to warrant the split.
Sizes: shell.cpp 497 → 448, commands.cpp 846 → 675 (+ 191 for the
new commands_export.cpp). DESIGN.md updated.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New user-facing features:
- `export connections <file>` writes a tabular dump of every wire pair:
connection, transform, left/right module/part/pin/signal/type/suspect,
mixed-types flag. Dispatch on extension: `.csv` (flat file) or `.ods`
(one sheet per connection). Any other extension shows an error and
writes nothing.
- Bare `export` (or dashboard `[x]`, or palette `export`) opens an
interactive file-picker dialog with a CSV/ODS toggle at the top.
Picking a filter rewrites the filename's extension. Last-used
directory and filename are remembered per-call-site.
- Two new CLI flags on the binary: `--source FILE` to run a script at
boot, `--restore FILE` to restore a snapshot at boot. Combinable.
Reusable infrastructure:
- `OdsWriter` (`src/imports/ods_writer.{hpp,cpp}`): minimal .ods writer
using libzip + pugixml (already in the build for the importer).
Multi-sheet workbook of string cells. ~180 lines, no new dep.
- Generic file-picker dialog (`screen_filedialog.cpp`): one Modal
reused for any "pick a path" interaction via
`OpenFileDialog(title, persist_key, default_filename, filters, cb)`.
Validates the picked extension against the filter whitelist;
unknown ones stay in the dialog with a status message. Persists
(dir, filename) per `persist_key`.
- Generic error modal (`screen_error.cpp`, `ShowError(msg)`): centred
red-titled popup, dismissable with Esc/Enter. Used by the export
failures (open-for-write, ODS save, unknown extension/kind);
ready for adoption elsewhere.
- Per-key path persistence (`SaveLastUsed`/`LoadLastUsed` in
`shell.cpp`): two-line file per key under the user-data dir.
- `UserDataDir()` extracted from the history path helper so the new
per-key persistence shares the same XDG/AppData logic.
- New help-screen topic "Export"; user-facing `doc/user/analysis.md`
gains an "Exporting" section; `DESIGN.md` gains a generics
section covering the dialog / error modal / persistence / ODS
writer; `DumpCommandsMd` now respects the `hidden` flag (the
`connect` alias no longer appears in the auto-gen reference).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`help` bare went back to printing the textual list of commands rather
than opening the help screen — the screen is reachable from the
dashboard with `[h]`. This matches how every other CLI handles `help`
and avoids surprising script behaviour.
Added a `hidden` field on `CommandSpec` so registry-level aliases can
be excluded from the listing. `connect` is now hidden (the alias
`plug` is the user-facing name on the dashboard and in `help`).
Both names continue to resolve to the same action; existing scripts
that used `connect` still work.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The analyze screen already used `[Suspect Power]`; the dashboard and
the `load` summary still said 'refuted', which felt harsher and was
out of sync. Now consistent everywhere.
- Dashboard module row: `power: X confirmed, Y suspect gnd: K`.
- Load summary: `types: N power, M gnd, K suspect Power (name only
— kept as Other)`.
Internal variable renamed `n_pwr_refuted` → `n_pwr_suspect`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- New `screen_help.cpp` (`screen_idx = 6`). Left column: menu of 13
topics (Overview, Dashboard, Console, Palette, Explore,
Connect/plug, set-connector-type, Signal types, NC pins, Analyze,
Scripting, Save/restore, Quitting). Centre column: paragraphs of
the focused topic, word-wrapped via `paragraph()` and scrollable.
Right column: standard help panel.
- `help` bare → opens the screen; `help <name>` keeps the existing
textual command-help behaviour for scripts.
- Dashboard `[h]` shortcut opens the screen, and the dashboard help
panel (both the loaded and the no-system branch) lists it.
- Console: title gets the standard breadcrumb (`essim → console —
type commands, read textual output`). Module/connection counters
moved off (they live on the dashboard now).
- Explore Enter on a part jumps to `set-connector-type` with the
exact-match index pre-computed in the filtered list (avoids the
substring-match collision where `J20` would land on the wrong
row when J200/J21 also matched).
- set-connector-type screen: bind `focused_entry` to `selected` on
both menus so the cursor `>` tracks the selected row when state
is pre-seeded from outside. Right column drops its strict
`size(WIDTH, EQUAL, 40)` in favour of `flex`, and the `new type`
input uses `xflex` so it actually stretches across the column.
- Esc on `set-connector-type` honours `screen_back_idx` — when
entered via Enter on a part in `explore`, Esc returns to explore;
otherwise it returns to the dashboard like every other screen.
Standalone command entries explicitly reset the back-link.
- Net-member rows in the explore detail pane carry a
`module\tsignal` payload so Enter opens the popup scoped to the
peer module rather than mis-firing on the locally selected one.
Same scheme for local-pin rows.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`explore` was already a superset of `search` (4 columns: module → type
→ filtered children → detail, with parts/signals/connections — vs
search's 2 columns of parts/signals only). It now also subsumes the
former `net` screen: when a signal entry is selected, the detail pane
shows the local pins followed by a `Net members (across connections)`
section listing every `(module, signal, type)` reachable through the
BFS over `Connection::pin_map`, with the count + dominant type and an
INCONSISTENT flag in the signal-detail header.
Removed:
- `src/tui/screen_search.cpp`, `src/tui/screen_net.cpp`.
- `commands["search"]`, `commands["net"]` (including its textual
inline form). The `find_net` / `Net` API stays for explore's BFS
panel and the analyze screen's net-mix check.
- `[s]` and `[n]` letter shortcuts on the dashboard.
- `net_*` and `search_*` state members + builders + constructor
inits.
screen_idx renumbering (the slots vacated by search + net are
removed, not left dead):
0 = console (unchanged)
1 = connect
2 = set-connector-type
3 = explore (unchanged number, but now subsumes search + net)
4 = dashboard (boot)
5 = analyze
Palette signal items now jump to `explore` prefilled on the signals
tab with the child filter seeded to the exact signal name; the BFS
section in the detail pane is what shows the cross-module net.
Net-member rows in the detail pane are deliberately read-only for
now (Enter is a no-op): the signal-type popup is scoped to the
currently selected module, so opening it on a peer-module member
would mis-fire. Cross-module Enter navigation can come later if
needed.
DESIGN.md and user docs updated accordingly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace `◀` / `▶` (often rendered as double-width emoji glyphs) with
`←` / `→` (single-cell in every monospace font) so the box borders
line up correctly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Command renamed from `set-type` to `set-connector-type` for
clarity (the previous name was ambiguous — "type" of what?). No
legacy alias kept; old scripts that still used `set-type` must be
migrated. `test/system.essim` and all user/design docs updated.
- Help panel (RenderHelpPanel) now wraps in borderRounded with a
centred bold title, so it is visually distinct from the main
content on every screen. Width bumped from 30 to 32 to include
the border.
- Analyze screen's Types tab gains a sibling "type glossary" panel
(also borderRounded, only visible when the Types tab is focused)
that explains Power / Suspect Power / Hard floor / Gnd in plain
language using `paragraph()` for clean word-wrap.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
UI restructuring:
- Dashboard (`screen_dashboard.cpp`, `screen_idx = 6`) is the new home
screen at boot. Reads Overview / Health / Analysis / Modules from
the current System every frame; per-module rows list parts grouped
by `connector_type` and a Power/Gnd inference summary (yellow when
any name-Power signal is refuted). Scrollable via PgUp/PgDn/Home/End.
Letter shortcuts: `c`=console, `s`=search, `p`=plug (alias of
connect), `t`=set-type, `e`=explore, `n`=net, `a`=analyze, `q`=quit.
- Global Ctrl-P palette (`screen_palette.cpp`) — fuzzy-finds over
registered commands + module / signal names. Activation runs the
bare command or jumps to the matching screen with state seeded.
- Unified analyze screen (`screen_analyze.cpp`, `screen_idx = 7`):
tabbed layout (`Issues / Groups / Types`), Tab or ←→ to switch
tabs, ↑/↓ to navigate the focused list. Replaces the previous
shell-bouncing `[v]erify` shortcut — `verify` content is now in
the Issues tab. Types tab attaches the decision rationale to each
signal row (fan-out / voltage / hard floor).
- Context help panel: `RenderHelpPanel(title, entries)` in
`tui_helpers.{hpp,cpp}` rendered on the right of every screen.
- Console (former "log") rename: screen 0 is `[c]onsole` in the UI
and "console" in its help-panel title. The underlying screen and
the shell prompt are unchanged.
- Esc from any non-home screen returns to the dashboard. The
dashboard itself swallows Esc; quit via `q` / the `quit` command.
`quit` now calls `screen_ptr->Exit()` directly so it works from
any screen including via the palette.
Signal type inference:
- `Signal::type` defaults to `Other` — auto-inference no longer
happens at construction.
- `infer_signal_types(System*)` is called at the end of every load.
Three rules: GndShield from name alone; Power requires name match
+ a hard fan-out floor (< 3 pins = always Other, regardless of
name or voltage) + at least one positive structural signal
(fan-out ≥ 4 OR voltage pattern in the name like `3V3`, `5V`).
- Thresholds exposed in `analysis.hpp` (`POWER_FANOUT_HARD_FLOOR`,
`POWER_FANOUT_CONFIRM_MIN`, `has_voltage_pattern`) so the analyze
screen can render the same rationale without duplicating logic.
- `set-signal-type` still wins; save/restore round-trips the type.
Analysis groups & anomalies:
- New `GroupKind::DiffBus` — ≥ 2 diff pairs sharing the same
outer-stem with consecutive integer indices are aggregated into a
single bus (`MDI[0..3]_P/N`). `MDI0` and `PCIE_TX_0` index forms
both accepted. Solo pairs under a bus-able stem fall back to
`DiffPair`.
- New `AnomalyKind::DiffBusGap` for missing lanes.
Documentation:
- `DESIGN.md`: dedicated "Categorization rules (normative)" section
consolidating signal type, NC origin, signal groups, anomalies,
component kind, and connector wiring rules with exact thresholds
and decision order.
- `doc/user/analysis.md` (new): user-facing version of the same
rules in plain language. Linked from `doc/user/index.md`.
Tests: +6 new cases (62 total). Adjusted `test_persist.cpp` to set
the signal type explicitly in the fixture (no more auto-inference).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- New `src/system/analysis.{hpp,cpp}` — stateless post-processing pass
`analyze_system(System*) → AnalysisReport`. Per-module detection of
signal groups and anomalies; pure read, re-runnable.
- Groups: diff pairs (`*_P` / `*_N`, case-insensitive), buses
(`NAME[N]` or strict `NAME_N` — the `_` before digits is required
so names like `GETH_01_VDD12` are not misread as a bus).
- Anomalies: `DiffPairOrphan` (asymmetric: only `_P` without `_N` is
reported — `_N` alone is overloaded with active-low semantics and
floods the output with false positives), `BusGap` (missing index
inside a detected `[lo..hi]`).
- Noise filters: signals starting with `$` (Mentor internals) are
skipped wholesale.
- New `analyze` shell command — prints groups sorted by module +
label, then anomalies. Sized for the upcoming dashboard.
- `tests/test_analysis.cpp` — 8 cases covering both detectors, false-
positive guards (no-underscore digits, `$`-prefixed internals), and
per-module scoping.
- `tests/test_nc_origin.cpp` — completes the prior NC-tagging commit
with round-trip + drop_singleton_signals coverage.
- DESIGN.md updated: layout entry for `analysis.{hpp,cpp}` and new
section explaining the pass; NC-origin paragraph aligned with the
actual tag semantics and the verify three-pass summary.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Enter on a signal entry (net / explore) opens a modal popup to pick
power / gnd / other. Recording is deduped: a sequence of toggles on
the same signal collapses to a single `set-signal-type` line; no-op
selections record nothing.
- Bare interactive commands (the ones that open a full-screen mode)
are no longer recorded by `script-save`. Their inline forms still
are. Mutating actions inside a screen record their own canonical
line.
- Mentor importer treats signals whose name starts with `unconnected`
as no-connect — the pin is kept on the part without a signal and
tagged `ImportedUnconnected`.
- `drop_singleton_signals` runs at the end of `load`: any signal with
exactly one pin is detached (singletons are NC by definition); the
pin is tagged `DroppedSingleton`. Count is reported inline.
- `verify` gains a one-line orphan summary (imported NC / dropped
singleton totals). Pins materialised by `FillIdentityNCs` are
excluded via a `pin_map` filter — they are bridged to a real signal
on the peer module and are not real NCs at system level.
- NcOrigin tag is serialized in save snapshots as an optional 4th
field on N records (backward-compatible).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- `essim --commands-md [file]` instantiates the Tui, calls
`Tui::DumpCommandsMd(ostream&)` which iterates the live registry and
emits Markdown grouped by interactive/other, then exits. Single
source of truth: a new `CommandSpec` field surfaces automatically.
- CMake `doc` target now `DEPENDS essim` and chains:
doxygen → gen_api_md.py → doc/api/
essim --commands-md → doc/user/commands.md
- `doc/user/` adds:
- index.md (hand-written) — first session, interactive-screen
conventions, save/restore/replay overview.
- scripting.md (hand-written) — `set`/`$var` expansion semantics,
`source` event-paced execution, script-save denylist, worked
example pointing at test/system.essim.
- commands.md (auto-generated, regenerated by the `doc` target).
- Top-level README refocused on quick start; pointers to the new
doc tree (user/, api/, DESIGN.md) instead of an inline command table.
- doc/README.md and DESIGN.md document the two-pipeline doc workflow.
- `test/system.essim` and user docs anonymised: bkp → backplane,
vdn1/2/3 → payload1/2/3, cb3p → payload4, bpb/cob/ssu →
peripheral1/2/3; netlist file names + variable names + paths all
replaced with generic equivalents.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`cmake --build build --target doc` runs Doxygen to produce XML, then
`doc/gen_api_md.py` (~330 lines, stdlib-only) emits a Markdown tree
under `doc/api/` that gitea renders directly in its file browser.
- 24 class/struct pages + 51 source-file pages + indices, with source
links of the form `../../../../src/...#L42` that gitea turns into
clickable line-anchored links.
- Doxyfile.in templated by CMake (XML-only output to build/doc/xml/).
- Pure Python emitter, zero external deps — no doxybook2 (not packaged
on Arch) and no moxygen (avoids Node).
- Target gracefully disabled if Doxygen or Python 3 is missing at
configure time; regular build target unaffected.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- New `net` full-screen layout (`screen_net.cpp`, `screen_idx = 5`): three
columns (module menu / signal filter + menu / live BFS result). Bare
`net` opens the screen; `net <m> <s>` keeps the inline path.
- Main screen grows a title bar: " essim — system digital twin "
(bold + dim) on the left, live "N module(s), M connection(s)" on
the right.
- Every interactive screen now renders the same breadcrumb at the top:
" essim → <name> — <short description> ", followed by a separator.
- `tui_helpers.hpp` exports `FocusLabel(elem, focused)`. Every
interactive screen wraps its field labels with it so the active
field's label flips to inverted video. Buttons (Connect, Apply)
invert as a whole.
- `CommandSpec` gains a `bool interactive`. `help` (no args) splits
the listing into "Interactive (open a full-screen mode)" and
"Other". `help <name>` tags interactive entries with [interactive]
and explains the bare-vs-inline duality.
- `DESIGN.md` (renamed from `CLAUDE.md`): refreshed Layout, TUI, and
screen-recipe sections to cover the new field, the title idiom,
FocusLabel, the `net` screen, and the event-paced `Computing…`
modal during `source`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Major additions, all wired end-to-end with doctest coverage:
- Altium netlist importer (`imports/import_altium.{hpp,cpp}`): two-pass
parser for `[ ]` parts and `( )` signals; `System::Load` no longer has
the IMPORT_ALTIUM hole.
- `duplicate <src> <dst>` deep-copies a module (signals, parts, pins,
rewired signals); connections excluded by design.
- Nets (`system/nets.{hpp,cpp}`): BFS over `Connection::pin_map` to
return the transitive (Module, Signal) closure. `verify` extended with
a second pass flagging Power↔GndShield inconsistencies in bridged
nets; new `net <module> <signal>` command for inspection.
- Canonical pin names (`system/pin_name.{hpp,cpp}`): zero-padded digit
suffix lets A1 ↔ A001 pair via `IdentityTransform` and
`CheckIdentityCompatible` without losing the imported notation.
- Component classification (`system/component_kind.{hpp,cpp}`):
`Part::kind` inferred at construction from the reference-designator
prefix (longest-match: LED/TP/SW/FB/MK/MP/MH/HS/RA/RN/RP/RV first,
then R/C/L/F/D/Q/U/J/P/Y/X/S).
- Identity wiring tolerance: `CheckIdentityCompatible` accepts the
subset case (typical when one importer drops NC pins, e.g. Altium)
and surfaces orphans as an info string. `FillIdentityNCs`
materialises orphan canonical positions as NC pins on the missing
side at connect time.
- Connector layout preparation: `pin_layout(kind)` and
`FillPartFromLayout(part, kind)` stubs in `pin_role`, called from
`set-type`. Empty today; populate alongside `vpx_3u_role`.
- TUI scrollback: PageUp/PageDown step 10 lines, Home/End jump to
ends; `Print()` snaps back to the tail.
- `set <name> <value>` declares session variables; `$name` / `${name}`
expanded inside `Finalize` between canonical-form recording and the
action call — history and script-save preserve `$var` references.
- Long `source` scripts now show a centred "Computing…" modal with a
N/M progress counter. Driven by a ticker thread that posts one
paced `Event::Special` per processed line, ack'd by the main thread,
so heavy lines don't backlog ticks and freeze the counter.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add LICENSE (full EUPL v1.2 text from the SPDX archive) and a README
covering build, commands, tests, project layout, and the licence
notice. Project is now declared open-source under EUPL-1.2.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Domain
- Signal carries a SignalType (Power/GndShield/Other), auto-inferred
from the name in Signal::Signal via infer_signal_type. Override with
the new `set-signal-type` command.
- SignalType extracted to its own header so Pin can store an
`expected_signal_type` without a pins↔signals include cycle.
- pin_role(connector_type, pin_name) → SignalType lookup, called from
set-type to populate each Pin's expected_signal_type. The VPX 3U
table is currently a stub (returns Other).
- New `verify` command walks typed parts and reports pins whose
connected signal's type doesn't match the expectation.
- ODS importer no longer drops pins with empty signal column — they
stay in the part as NC, matching the rule "a pin is either NC or
connected to a signal".
- persist: new S tag for non-default signal type overrides.
Tests
- doctest v2.4.11 via FetchContent (with CMAKE_POLICY_VERSION_MINIMUM
shim, doctest's CMakeLists has a too-old floor for current CMake).
- Source files moved into a static library `essim_lib` so both `essim`
and `essim_tests` reuse the same compilation. main.cpp is the only
file kept out of the lib.
- Layer 1 (pure helpers): ToLower, LongestCommonPrefix, Tokenize,
NaturalLess (numeric/case/leading-zero edge cases + total-order
invariants), signal_type round-trips and infer_signal_type families,
VpxTransform registry + symmetry + reference-table mapping for
connector P0 row 1, IdentityTransform same-name wiring.
- Layer 2 (round-trip): build a synthetic 2-module system in code,
save → restore → assert modules / parts / connector_types / NC pins
/ signal type overrides / connections + pin_map are all preserved.
- Tui::Tokenize moved to a free function in tui_helpers so tests can
call it without dragging ftxui into the unit-test layer.
- 27 test cases, 123 assertions, ~150 ms.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- ODS importer (libzip + pugixml): each sheet → Part, rows → Pin/Signal.
- save / restore commands: tab-delimited snapshot of modules, parts,
signals, connections + pin_map. `restore` replaces the System.
- source / script-save: replay a file of commands; record canonical
commands since last `new` for replay later. Interactive screens
refused during source. `explore` marked non-scriptable.
- TUI screen_explore: 4 columns (modules, type, children, detail) with
filters on children and detail; detail is a Menu so arrows scroll
long pin lists.
- Connector types & transforms: each Part carries a `connector_type`
string. `set-type` validates the part's pin layout against the type
(cols set check). `connect` strict pair: rejects when lookup falls
back to identity unless types are both empty AND pin sets match.
- VPX 3U transforms: 3 registered pairs (vpx-3u-bkp-pN ↔ vpx-3u-payload-pN,
N=0/1/2) with row-pattern correspondence tables ported from the user's
Python reference.
- Code split for maintainability: src/tui/{shell,completion,commands,
screen_main,screen_search,screen_connect,screen_settype,screen_explore,
tui_helpers}.cpp.
- Bug fixes: Module::add(Part*) override sets part->prnt (was always
null, breaking save's W lines). Defensive guards in explore against
empty Menu lists. Renderer wrapped in try/catch so domain throws
surface as on-screen errors instead of SIGABRT.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- ftxui v6.1.9 fetched at configure time; vendored .a + headers dropped.
- TUI: visualisation area on top, input prompt at the bottom; ↑/↓ history,
Tab completion (commands + file paths), Esc cancels prompts. History is
persisted (XDG on Linux, %LOCALAPPDATA% on Windows when ported).
- Command registry: `new`, `load`, `search`, `clear`, `help`, `quit`/`exit`.
Inline params or interactive prompts; either way the canonical inline
form is what gets stored in history.
- `search`: second full-screen mode with module + parts/signals menus and
a live-filtered list; Tab cycles focus, Esc returns to the main shell.
- Domain: `System::modules()` accessor + `SystemElementContainer::size()`
to support load summary + search.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>