Parity with Capital Device Modeler 2512.2608
Every stock feature, its state in the replacement, and the evidence. Status words:
Done (built and exercised on live Capital, result read back independently through the stock
app's session), Built (in the app, not yet exercised against Capital), Gap (not built),
N/A (not a Device Modeler feature, or broken in the stock app too). Test objects are all named
ZZTEST_*. Last updated 2026-09-24.
ICD
| Feature | Status | Evidence |
|---|---|---|
| ICD list, search by text | Done | 13 stock ICDs listed with release level and part number |
| View ICD, all stock fields and sections | Done | AUDIO UNIT:A field-for-field vs stock page text |
| New ICD (uid minted up front) | Done | ZZTEST_ICD2:A created from the UI, bulk pins 1-3, two nets on one pin |
| Edit fields, tags, effectivity, assigned to | Done | description, Assigned To, pin option G2 saved and read back |
| Pins: add, bulk add, delete, rename, several nets per pin | Done | pin 2 -> ZZTEST_NET2 + ZZTEST_NET3 read back |
| Net attributes: shield, signal group, option expression | Done | shield true, signal group GRP/AUDIO read back; net option via the dialog |
| Pin and net user properties | Done | PinColor=Red on pin 1, NetGauge=22AWG on its net read back |
| ICD user properties (rich-text ones read-only) | Done | ZZ Note=hello read back |
| Option expression dialog (project options, operators, validation) | Done | G1..G7 listed, bogus name rejected, G1 \|\| G2 saved |
| Applicability (Design tags), Projects (Project tags), Domains | Done | 2-Audio, Aerospace ELA Power Design saved; no domains exist here |
| Release level change along allowed transitions | Done | A1 Draft -> Obsolete (form locks) -> Draft |
| Edit locked by release level behaviour / permissions | Done | Obsolete shows read-only with the stock reason |
| Assign part (syncWithPart), clear part | Done | ZZTEST_ICD2_copy + ZZTEST_DEV3: DEV, ZZ_FP, ZZ-SPN-1, ZZ-CPN-1, pins A1/A2 |
| Copy / Revise / Create descendant (unsaved template, then Create) | Done | ZZTEST_ICD2_copy:A, ZZTEST_ICD2:A1, ZZTEST_ICD2_child:A (parentRef read back) |
| Ancestor ICD picker (candidateparenticds) | Done | picked ZZTEST_ICD1:A from 18 candidates in the UI; parentRef read back |
| Delete | Done | copy deleted; stock query no longer finds it |
| Export (.zip) | Done | 11913-byte zip downloaded from the UI |
| Import (.zip), Import Merge | Done | exported zip re-imported (part, pins, tags, effectivity kept); merge updated in place |
| XSD import (Library Synchronizer) | Built | no XSD file available to test |
| Create component from ICD | Done | ZZTEST_DEV_FROM_ICD2 created and assigned from the UI |
| ICD Table Editor (multi-edit) | Done | role changed on ZZTEST_ICD2:A, pins untouched |
| Harness Footprint (device connector footprint, pin -> connector cavity) | Done | J1 + J2 clones of C-81334, 1->J1.1, 2->J1.2, 3->J2.5 read back; duplicate cavity refused; unused clone dropped. With a part assigned the part footprint shows read-only, as stock |
| Backshell signals (nets on a connector backshell termination) | Done | test backshell BS1 on J1 created by direct PUT (stock has no create action; it renders it after a full reload); nets edited from the UI (ZZTEST_NET3 added) and read back with the backshell and footprint intact; footprint locked while a backshell exists, as stock |
| ICD query builder (power queries) | Done | site icdqueries.json used when present (absent here, where stock shows none); built-in set otherwise. Applicable ICDs 2-Audio -> 17, By Role ZZTEST -> 5 |
| Net filter inside the ICD form (wildcards * ?) | Done | Z*1A -> pin 1 only; ZZTEST_NET? -> pins 1, 2 |
| Deep links | Done | hash form /devices/#/ICD/editICD/{uid} opens the ICD from a cold load (AUDIO UNIT:A). The path form /devices/{name}/ICD/editICD/{uid} returns 404 from the stock server too (checked 2026-09-24); the replacement still redirects it to the hash form when it is served. Not tested from the ConnectivityHub plugin itself |
Library
| Feature | Status | Evidence |
|---|---|---|
| Latest devices, part-number search | Done | 27 latest; Enter queries Capital (finds new parts the fixed list omits) |
| New device | Done | ZZTEST_DEV3 |
| Edit: pins, properties by type code, footprints + pin mapping to connector cavity, customer / supplier PNs, extra | Done | all read back on ZZTEST_DEV3; cavity 1 of C-81334 resolved |
| Copy / Revise device | Done | ZZTEST_DEV4, ZZTEST_DEV4:B |
| Type code change warning dialog (Remove / Keep / Revert) | Done | ZZTEST_DEV3 DEV -> other type listed CAGE CODE; Revert kept DEV, nothing dirty. Orphan properties editable to unset |
| Library query builder (power queries) | Done | site libraryqueries.json or built-in set; partnumber=* added (Capital ignores other keys without it). Type code NOPE -> 0, ZZTEST* -> 8 |
| Select mode (from ICD assign part) | Done | ICD "View/Select part" picker |
Dictionary
| Feature | Status | Evidence |
|---|---|---|
| Devices, Logic Designs, Nets grids with column filters and sort | Done | 14 / 5 / 116 |
| Lock / unlock (client side), add, edit, delete, rename, user properties | Done | modifiedTime 178, 179 |
| Saved filters (per browser) | Done | "ZZ speakers" (2N-SPKR) saved, survived a reload, re-applied 117 -> 12; localStorage, as stock |
| Validation (lengths, unique names) | Done | duplicate blocked |
| Import / Import Merge / Export | N/A | 404 on the devices dictionary in the stock app too |
| Signals / Messages (network dictionary) | N/A | separate Capital Dictionary app |
Connectivity (new)
| Feature | Status | Evidence |
|---|---|---|
| All ICDs / one ICD / one net views | Done | 88 -> 36 bends on the audio sample |
| Straight, pin-facing wires | Done | all 1:1 wires straight |
| Filters: applicability, project, build list, effectivity, options (ICD, pin and net level) | Done except build list | effectivity 16 -> 3; project + G3 hides, G1 shows; net G2 hidden under G1 |
| Build list filter | Built | no ICD carries a build-list tag, so nothing to match |
| From / To table with expandable details | Done | 200 rows drawn, "Show all", CSV export of every row |
| Checks (new, 0.9.3) | Done offline | nets that go nowhere, near-duplicate names, connections no option combination allows, ends that never connect, non-overlapping effectivities, duplicate pins, unwired pins, nets not in the dictionary, several revisions in view. Live data: 15 genuine findings. Error branches exercised only on built-to-fail input (tools/logic_selftest.mjs, 20/20 incl. a clean control) because live data triggers none |
| Compare revisions (new, 0.9.3) | Done offline | ZZTEST_ICD1 A vs A1 matches an independent diff of the raw data; pin/net/option branches exercised on built input |
| Group by applicability / project / role (new, 0.9.3) | Done offline | synthetic 300 ICDs grouped: 10 boxes, 0.4 s, 0 through, 0 overlaps |
| Layout off the main thread (new, 0.9.3) | Done | ELK in its own worker, routing in a second; status line says which ran. Main-thread lag on a 300-ICD relayout 1.36 s (MessageChannel heartbeat, 300 ms control caught); the rest is SVG rendering |
| Export SVG, saved views, Copy for ConnectivityHub (new, 0.9.3) | Done offline | exported SVG parses, 20 boxes / 70 wires, no hit paths; saved view round trip in logic test. Capital desktop registers no URL protocol, so no direct link into Logic Designer or ConnectivityHub is possible |
Repeatable proof (Phase 4)
#/selftest replays the app's own write paths against live Capital on throwaway ZZTEST_SELFTEST*
objects and checks each result by an independent read-back: ICD create (two nets on one pin), edit
(effectivity, Design tag, ICD and net options), harness footprint (J1.1), revise, table edit, delete
(empty-200 read as missing), library device save, dictionary add + remove. It carries two negative
controls that must fail, and cleans up its ICDs and dictionary entries (cleanup confirmed from the
stock session). 2026-09-24: 10 of 10 passed. Re-run after every Capital update.
The per-row differential against the stock app is the Evidence column above: every Done write was read back through the stock app's own session, and the footprint and backshell shapes were first captured from the stock model itself.
Side-by-side run, 2026-09-24
Same actions driven through the UI of the unmodified stock war (served by
tools/prod_preview.py --root <extracted stock war> --port 5193) and of the replacement (5191), both
upstream of tools/capture_proxy.py, so every request each app sent is on disk (session
captures/20260924_160213). Compared on the request body and on the object Capital stored.
| Action | Stock request | Ours | Result |
|---|---|---|---|
| Create ICD: 2 pins on one net, Design tag, effectivity | 00265 POST /icds |
00274 | Pass: 41 fields, only the name differs plus one extra empty attributes.percentComplete from ours (accepted, stored identically). Stored objects: 0 differences outside id/time/name; control against an unrelated ICD: 8 |
| Dictionary: add a net with a short description | 00297 PUT devicesdictionary |
00311 | Failed first (00302: ours sent name Z and modified 10-AJARLT-1; added rows re-sorted while typed). Fixed in 0.9.2, server data restored and read back. Rerun: 16 fields, only name and modifiedTime differ; stored entries identical |
| Delete ICD | 00328 DELETE /icds/{uid} |
00335 | Pass: same request, 204; both absent by query (control query on an existing ICD returns 2) |
| Library device: edit description | 00354 PUT /parts/devices/{uid} |
00362 | Pass: 43 fields, only the value typed and modifiedTime differ |
Also found: stock sorts names with its own natural comparator (1N-... before 10-...). Ported to
lib/sortOrder.ts, identical to the stock function on all 16641 pairs of live net names; applied to the
dictionary, and (0.9.3) the ICD list, table editor, cavity pickers and pin grid. The stock code applies
this comparator to cavities and footprint pins; its master lists arrive in server order from the same
queries ours sends. The ICD master list order in stock was not observed directly (DERIVED). Content-Type
differs only in case (application/JSON vs application/json).
Scripted request parity (0.9.3)
node tools/request_parity.mjs runs our request-building code on the inputs of every write the stock
app made in the captures and diffs the result. 23 stock writes across 11 operations match: ICD create
(blank and from a copy/revise template), edit, copy, revise, descendant, create component, table editor,
device create and edit, dictionary add. Accepted, with reasons in the script: an empty
attributes.percentComplete on create, and numCavities sent as a string (stock itself sends a string
on create and a number on edit). A planted change is caught. Not covered: import/export (multipart
files), dictionary delete (no stock capture).
Not yet done
- Phase 5 cutover: DONE 2026-09-24, 0.9.0 live at
/devices/(evidence indocs/DEPLOY.md). - Side-by-side: 4 actions by hand (above) plus 23 stock writes by script. Not covered: import/export, dictionary delete.
- Capital contract check (self-test page): built, snapshot recorded, control passes offline; not yet run against live Capital (session expired when it was ready).
- Layout beyond ~100 ICDs ungrouped: synthetic 300 ICDs lays out (27 s) with 4 wires through boxes and 5191 overlapping pairs; grouping is offered above 60 ICDs and is clean. 100 ICDs: 0 through, 6 overlaps.
- XSD import: no sample file; the format is server-defined and undocumented (stock action
createImportXSDConnectorAction, server messages expect ICD, connector, signal and backshell signal names). Candidate schemaapps/dtd/bridgesicds.xsdis unconfirmed. - Build-list filter: stock tag context is
BuildList(CAPITAL_APPLICABILITY_BUILDLIST_TAG), which the filter matches, but none of the 116 projects has a build list, so it cannot be exercised yet. - Layout (0.9.1, not yet deployed): measured with
node tools/layout_metrics.mjs <fixture>, which runs the real layout in Node in the app's input order and matches the live page's own counts. The 0.9.0 layout on live data: 56 bends, 17 wire segments through boxes, 2 overlapping pairs, 2 pins on a top edge. 0.9.1: 30 bends, 0 through boxes, 0 overlaps, 0 off-edge pins (audio fixture 36 -> 26 bends, 13 -> 0 through). Changes: every pin faces its partner; 3+ endpoint nets draw as a bus with tees; a tee that would cross a box detours through a free track (the remaining bends); same-column pairs use a C instead of a loop below.