WireViz/docs/agent-threads/notebook-api/001-notebook-api-handoff.md
Ryan Malloy 65af27e0da Add notebook-ready API: graph cache invalidation, structured BOM, fragment merging
Three additions to support interactive/notebook-style harness building:

- Graph cache invalidation: _invalidate_graph() called from all mutating
  methods so svg/png output reflects latest state after mutations
- bom_list_dicts(): JSON-serializable BOM export as list of dicts
- parse(harness=, populate_bom=): append YAML fragments to existing
  harness for cell-by-cell building with deferred BOM population

Templates persist on the Harness object across parse() calls so
component definitions in one fragment are available to connections
in later fragments.

Includes 24 new tests covering all three features plus full incremental
workflow simulation. All 122 tests pass.
2026-02-13 07:08:21 -07:00

115 lines
3.9 KiB
Markdown

# Message 001
| Field | Value |
|-------|-------|
| From | notebook-api-agent |
| To | web-ui-agent |
| Date | 2026-02-13T00:00:00Z |
| Re | Notebook-Ready API additions to WireViz core |
---
Three new capabilities have been added to the WireViz core library on `testing/web-ui-features` to support interactive/notebook-style harness building. All changes are backward-compatible.
## 1. Graph Cache Invalidation (automatic)
The `Harness._graph` cache is now invalidated automatically whenever any mutating method is called (`add_connector`, `add_cable`, `add_additional_bom_item`, `add_mate_pin`, `add_mate_component`, `connect`).
**No action needed on your side.** After any mutation, accessing `harness.svg` or `harness.png` will regenerate the diagram. Previously, calling `.svg` after adding new components would return stale output.
```python
h.add_connector("X1", pins=[1, 2])
svg1 = h.svg # renders X1
h.add_connector("X2", pins=[1, 2])
svg2 = h.svg # automatically re-renders, now includes X2
```
## 2. Structured BOM Export via `bom_list_dicts()`
New function in `wireviz.wv_bom`:
```python
from wireviz.wv_bom import bom_list_dicts
dicts = bom_list_dicts(harness.bom)
# Returns: [{"#": 1, "Qty": 2, "Description": "...", ...}, ...]
```
- Returns `List[Dict]` (JSON-serializable)
- Each dict maps column header to cell value
- Empty BOM returns `[]`
- Safe for `json.dumps(dicts, default=str)`
- Keys vary based on which columns have data (empty columns like P/N are omitted, matching `bom_list()` behavior)
## 3. YAML Fragment Merging via `parse(harness=...)`
The `parse()` function now accepts two new optional parameters:
```python
from wireviz.wireviz import parse
parse(
fragment, # YAML string, dict, or file path
return_types="harness",
harness=existing_harness, # append to this harness
populate_bom=False, # skip BOM for intermediate fragments
)
```
### Cell-by-cell building pattern:
```python
from wireviz.wv_harness import Harness
from wireviz.wv_dataclasses import Metadata, Options, Tweak
from wireviz.wireviz import parse
from wireviz.wv_bom import bom_list_dicts
# Create empty harness
h = Harness(metadata=Metadata({}), options=Options(), tweak=Tweak())
# Cell 1: Define connectors
parse({"connectors": {"X1": {"pins": [1,2]}, "X2": {"pins": [1,2]}}},
return_types="harness", harness=h, populate_bom=False)
# Cell 2: Define cable
parse({"cables": {"W1": {"wirecount": 2, "colors": ["BK", "RD"]}}},
return_types="harness", harness=h, populate_bom=False)
# Cell 3: Connect and render
parse({"connections": [[{"X1": [1,2]}, {"W1": [1,2]}, {"X2": [1,2]}]]},
return_types="harness", harness=h, populate_bom=True)
# Now get outputs
svg_data = h.svg # rendered diagram
bom_data = bom_list_dicts(h.bom) # JSON-serializable BOM
```
### Mixed programmatic + YAML pattern:
```python
h = Harness(metadata=Metadata({}), options=Options(), tweak=Tweak())
h.add_connector("X1", pins=[1, 2]) # programmatic
# YAML fragment references existing X1
parse({"connectors": {"X2": {"pins": [1,2]}},
"cables": {"W1": {"wirecount": 2, "colors": ["BK","RD"]}},
"connections": [[{"X1": [1,2]}, {"W1": [1,2]}, {"X2": [1,2]}]]},
return_types="harness", harness=h, populate_bom=True)
```
### Key behaviors:
- When `harness` is provided, the fragment's `metadata`/`options`/`tweak` sections are ignored
- Templates defined in earlier fragments are available to later fragments (persisted on the harness)
- `populate_bom=False` skips BOM computation; call `h.populate_bom()` explicitly when ready
- Existing components can be re-referenced across fragments without re-definition
---
**Next steps for recipient:**
- [ ] Integrate `harness.svg` for live preview in notebook cells
- [ ] Use `bom_list_dicts()` for the BOM panel/table
- [ ] Implement cell-by-cell YAML parsing using `parse(harness=h, populate_bom=False)`
- [ ] Call `populate_bom=True` on final render or when BOM display is requested