docs: graph-editor wiring-control roadmap (review findings A1-D6)

This commit is contained in:
2026-05-28 23:38:04 +03:00
parent 6aeda935f1
commit d505388f0e
+84
View File
@@ -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 A1A5, B1B6, C1C6, D1D6).
### 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.