From 9d48292976a4369a31ca2ba86c07b35db5f31d7f Mon Sep 17 00:00:00 2001 From: Martino Ferrari Date: Wed, 24 Jun 2026 13:31:15 +0200 Subject: [PATCH] docs(logic): document panel-logic array locals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document array-valued local variables (statevar type=array with elem/sizing/capacity), the value-polymorphic expression engine and array functions, the array.* mutation nodes, accumulate/export unification, and index-aligned CSV export in TECHNICAL_SPEC §3.8. Note in TODO that panel-logic (Phase 1) array support is complete while the server-side control-logic port (Phase 2) remains pending. Co-Authored-By: Claude Opus 4.6 --- TODO.md | 1 + docs/TECHNICAL_SPEC.md | 32 +++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 8b91265..6a31ec3 100644 --- a/TODO.md +++ b/TODO.md @@ -98,6 +98,7 @@ - [x] Implement live / debug mode where value are evaluated online and visualized at human speed (e.g. 0.5s) — editor spins up a second client-side `LogicEngine` in new `dryRun` mode (real writes/config/dialogs suppressed) loaded with the edited graph; per-node values polled into the shared badges; the view-mode singleton is untouched - add full suppor to local array values: dynamic, dynamic but capped max, fixed size etc: - array functions should work with new local array + - NOTE: **Phase 1 (panel logic, client-side TS) is DONE** — array statevars (dynamic/capped/fixed), value-polymorphic expression engine with indexing + array functions, `action.array.*` mutation nodes (legacy accumulate/export/clear unified + auto-migrated), panel-XML round-trip, editor declaration form, and widget array modes (plot/table/multi-LED). Box stays unchecked until **Phase 2** (server-side `internal/controllogic` Go port, see below) lands. - [ ] Control loop: - [x] Implement live / debug mode where value are evaluated online and visualized at human speed (e.g. 0.5s) — server pushes per-node events over the WS (`debugSubscribe`/`debugNode`) via a new `DebugObserver` on the engine (lock-free `atomic.Value`, gated by a per-graph watch set) + `internal/server/debughub.go` (drop-on-full fan-out). Two modes share one message shape: **Live** observes the running enabled graph; **Simulate** dry-runs the unsaved edits in a throwaway sandbox (`StartSimulate`, side effects suppressed). Live/Sim sub-toggle in the editor - add full support to server side array values diff --git a/docs/TECHNICAL_SPEC.md b/docs/TECHNICAL_SPEC.md index 99a47c5..4b532f8 100644 --- a/docs/TECHNICAL_SPEC.md +++ b/docs/TECHNICAL_SPEC.md @@ -346,10 +346,40 @@ mount and `clear()` on unmount. The engine subscribes to every referenced signal cache, then on each trigger activation walks the wired graph (gates, if/loop, actions), evaluating expression fields via a small safe recursive-descent evaluator (no `eval`). Triggers include button/threshold/change/timer/loop and On-open/On-close lifecycle; actions -include signal writes, delay, log, in-memory data arrays (accumulate/export-CSV/clear) and +include signal writes, delay, log, array mutations (see below) and user dialogs (info/error/set-point). The engine runs entirely in the browser — no backend component. +**Array-valued local variables.** Panel-local variables (``) may be declared with +`type="array"`. An array statevar carries `elem` (`number`|`bool`|`array`, the element kind — +`array` gives a 2-D array), `sizing`, and `capacity`. The three sizing policies +(`web/src/lib/arraypolicy.ts`) are: **dynamic** (unbounded up to `ARRAY_MAX = 1_000_000`, +oldest dropped past the cap), **capped** (length kept ≤ `capacity`, oldest dropped FIFO), and +**fixed** (exactly `capacity` elements — truncated or zero-padded). Sizing is enforced only at +store time (`writeLocalState` → `applySizing`); expressions themselves are pure and produce +unbounded values. The value model is the tagged union `ArrVal = number | ArrVal[]` with +booleans represented as `1`/`0` at the leaves. + +The expression evaluator (`web/src/lib/expr.ts`) is value-polymorphic: `evalValue` returns an +`ArrVal`, while `evalExpr` returns a `number` (`NaN` if the result is an array). It supports +array literals `[a, b, c]`, indexing `a[i]` (negative indices count from the end), and a table +of array functions: `len`, `sum`, `mean`, `slice`, `concat`, `reverse`, `sort`, `scale`, `add`, +`sub`, `push`, `set`, `insert`, `remove`, `pop`, `shift`, `indexOf`, `contains`, `fill` (plus +`min`/`max`, which accept either scalars or an array). These are pure — they return new values +and never mutate a stored variable. + +Array mutation nodes write back to a declared array statevar: `action.array.push`, `…set`, +`…remove`, `…pop`, and `…clear`. The legacy `action.accumulate` and `action.clear` nodes are +retained as aliases over `action.array.push` / `action.array.clear`, and legacy graphs are +auto-migrated to declare their backing arrays on load (`ensureArrayDecls`). `action.export` +now produces **index-aligned** CSV: each configured array becomes a column (custom per-column +labels supported), rows aligned by element index. Array statevars and all array nodes +round-trip through the panel XML (``). + +> **Note:** This is Phase 1 (panel logic, client-side TypeScript). The equivalent array +> support in the server-side control-logic engine (`internal/controllogic`) is a separate +> Phase 2 effort and is not yet implemented. + ### 3.9 Control Logic Engine Control logic is server-side (`internal/controllogic`): always-on flow graphs that run under