docs: graph-editor wiring-control roadmap (review findings A1-D6)
This commit is contained in:
@@ -993,3 +993,87 @@ After phase 1 the codebase will have 3 fresh examples of "ping the LAN, listen f
|
||||
- LOW: Nanoleaf `.port` property added; pair-then-create E2E test
|
||||
added.
|
||||
- Tests: 1379 pass (+21 regression tests).
|
||||
|
||||
## Graph editor — "full control of wiring via graph" (in progress)
|
||||
|
||||
Goal: make the visual graph a first-class wiring control surface, not just a
|
||||
viewer. Driven by the ULTRA-DEEP review (findings A1–A5, B1–B6, C1–C6, D1–D6).
|
||||
|
||||
### Done (NOT yet committed — awaiting review/commit)
|
||||
|
||||
- [x] **A1** Undo/redo wired to connect/detach/move (was dead code); inverse ops
|
||||
throw on failure so the stack can't silently desync.
|
||||
- [x] **A2** Manual node layout persists to `localStorage` (`graph_node_positions`),
|
||||
cleared on relayout.
|
||||
- [x] **A3** Scene-preset disambiguation — deactivation scene now reachable via a
|
||||
field picker (was always picking the first match).
|
||||
- [x] **B6** Edge field labels (revealed on zoom ≥ 0.9).
|
||||
- [x] **C3** Health overlay — broken refs (referrer exists, target missing),
|
||||
dependency cycles, orphans; node warning badges + an issues toolbar button.
|
||||
- [x] **D1** `GET /api/v1/graph/schema` — authoritative connectable-field registry
|
||||
(`api/graph_schema.py`, pure + unit-tested).
|
||||
- [x] **D2** `GET /api/v1/graph` (nodes+edges+validation) and
|
||||
`GET /api/v1/graph/dependents/{kind}/{id}`.
|
||||
- [x] **D4** `POST /api/v1/graph/validate-connection` — existence + source-kind +
|
||||
cycle pre-flight; frontend validates before every write (fails open if the
|
||||
endpoint is unreachable). List/double-nested fields rejected.
|
||||
- [x] **B2** Drop-on-node connect — empty top-level slots are now wireable (drop a
|
||||
source onto any compatible node body, not just an existing port).
|
||||
- [x] **C4** Overwrite-occupied-slot confirm + delete-with-dependents warning
|
||||
(single delete only; bulk keeps the batch confirm).
|
||||
- [x] **D5** Create-and-connect — drag a port onto empty canvas → pick a compatible
|
||||
new entity kind → it's created and auto-wired (kind-scoped watcher).
|
||||
- [x] **D6 (read-only half)** "Export graph (JSON)" toolbar action.
|
||||
- [x] Custom per-entity `icon` + `icon_color` now render on graph nodes (parity
|
||||
with custom node colours; fallback to kind/subtype glyph).
|
||||
- [x] **B1** Edit single-level **BindableFloat** value slots from the graph
|
||||
(`brightness`, `smoothing`, `intensity`, `scale`, `speed`, … on
|
||||
color_strip_source; `brightness`/`transition` on output_target). Subtype-safe
|
||||
(only offers slots the target entity actually has). Writes the partial
|
||||
`{ <slot>: { source_id } }` payload → backend `Bindable*.apply_update` merges,
|
||||
preserving the static value. Verified data-safe (no `from_raw`/value-reset path).
|
||||
- Verification: `npm --prefix server run typecheck` + `run build` clean; ruff clean;
|
||||
graph backend tests 24 pass; full backend suite 1614 pass. 6 code-review passes,
|
||||
all CRITICAL/HIGH findings fixed.
|
||||
|
||||
### Left to do (deferred)
|
||||
|
||||
- [ ] **BindableColor slots** (`color`, `color_peak`, `fallback_color`,
|
||||
`default_color` on color_strip_source) — left non-editable in B1 because
|
||||
scalar-value-source → colour-slot semantics are unclear. **First check:** do
|
||||
colour-producing value sources exist? If a value_source can drive a colour,
|
||||
mark these 4 CONNECTION_MAP entries `bindable: true` (they already validate on
|
||||
the backend); the write path (`{ color: { source_id } }` → `BindableColor.apply_update`)
|
||||
is already value-preserving. If not, leave read-only.
|
||||
- [ ] **B4 — delete the frontend `CONNECTION_MAP` duplication.** Blocked on a backend
|
||||
write endpoint. Plan:
|
||||
1. Add `PUT /api/v1/graph/connection` (body: target_kind/id, field, source_id)
|
||||
that validates (reuse `validate_connection`) then APPLIES the write
|
||||
server-side — top-level via the owning store's update; single-level bindable
|
||||
via `apply_update`. Must reuse each entity's existing update path (validation,
|
||||
factory reconstruction, entity_changed event) — do NOT hand-roll per-store
|
||||
mutation. Highest regression risk; needs per-kind tests.
|
||||
2. Switch frontend `updateConnection`/`detachConnection` to call it.
|
||||
3. Have the frontend fetch `/graph/schema` and build ports/edges from it,
|
||||
then delete `CONNECTION_MAP` + the buildGraph edge duplication
|
||||
(graph-connections.ts / graph-layout.ts). Removes the 10-step sync checklist
|
||||
in `contexts/graph-editor.md`.
|
||||
- [ ] **D6 — blueprint import/instantiate.** Export exists; the apply half (serialize
|
||||
a selected subgraph's topology + entities, re-import with id remapping, conflict
|
||||
handling) is large and data-integrity-sensitive (see Data Migration Policy in
|
||||
CLAUDE.md). Scope as its own feature.
|
||||
- [ ] **List-slot editing** (composite `layers[]`, mapped `zones[]`, scene preset
|
||||
`targets[]`) — needs an element index in the write + validate paths
|
||||
(`validate_connection` currently rejects list fields). Edit via entity modal
|
||||
for now.
|
||||
|
||||
### Notes / decisions
|
||||
|
||||
- The backend `CONNECTION_SCHEMA` (`api/graph_schema.py`) is the authoritative
|
||||
superset; it already declares the bindable + list + value_source-chain edges. The
|
||||
frontend `CONNECTION_MAP` still owns write-routing (endpoint/cache) — that's the
|
||||
only reason it survives (see B4).
|
||||
- Bindable edges render dashed (`.graph-edge-nested`) but ARE editable — the dashed
|
||||
style intentionally distinguishes value bindings from structural edges.
|
||||
- `validate-connection` and `dependents` fail **open/safe** on the frontend so the
|
||||
graph keeps working against an older server without these endpoints.
|
||||
|
||||
Reference in New Issue
Block a user