diff --git a/internal/controllogic/debug.go b/internal/controllogic/debug.go index 3a11715..4254722 100644 --- a/internal/controllogic/debug.go +++ b/internal/controllogic/debug.go @@ -12,11 +12,11 @@ import ( // only meaningful when HasValue is true (e.g. an action.write's written value or // a flow.if branch as 0/1); otherwise the event just marks the node as active. type DebugEvent struct { - GraphID string `json:"graphId"` - NodeID string `json:"nodeId"` - Value float64 `json:"value"` - HasValue bool `json:"hasValue"` - TS int64 `json:"ts"` // unix millis + GraphID string `json:"graphId"` + NodeID string `json:"nodeId"` + Value any `json:"value"` + HasValue bool `json:"hasValue"` + TS int64 `json:"ts"` // unix millis } // DebugObserver receives node-execution events from running (and simulated) @@ -83,7 +83,7 @@ func (e *Engine) FireTrigger(graphID, triggerID string) bool { // emitDebug reports a node execution to the observer when the graph is watched // (or the graph is a simulate sandbox, which is always its own subscriber). -func (cg *compiledGraph) emitDebug(nodeID string, value float64, hasValue bool) { +func (cg *compiledGraph) emitDebug(nodeID string, value Value, hasValue bool) { if !cg.alwaysDebug { w, _ := cg.engine.debugWatch.Load().(map[string]bool) if !w[cg.id] { diff --git a/internal/controllogic/debug_test.go b/internal/controllogic/debug_test.go index c73f0bd..7b5aa12 100644 --- a/internal/controllogic/debug_test.go +++ b/internal/controllogic/debug_test.go @@ -79,7 +79,7 @@ func TestDebugObserverCapturesNodeValues(t *testing.T) { if !ok { t.Fatal("no event for write node 'w'") } - if !wEv.HasValue || wEv.Value != 42 { + if !wEv.HasValue || wEv.Value != 42.0 { t.Errorf("write node event = %+v, want value 42 hasValue true", wEv) } if wEv.GraphID != "g1" { @@ -136,7 +136,7 @@ func TestFireTriggerForcesRun(t *testing.T) { } time.Sleep(10 * time.Millisecond) } - if !wEv.HasValue || wEv.Value != 42 { + if !wEv.HasValue || wEv.Value != 42.0 { t.Errorf("write node event = %+v, want value 42 hasValue true", wEv) } @@ -187,7 +187,7 @@ func TestSimulateSuppressesWrites(t *testing.T) { if !ok { t.Fatal("simulate produced no event for write node 'w'") } - if !wEv.HasValue || wEv.Value != 42 { + if !wEv.HasValue || wEv.Value != 42.0 { t.Errorf("simulate write event = %+v, want value 42 hasValue true", wEv) } if wEv.GraphID != "sim" { diff --git a/internal/controllogic/engine.go b/internal/controllogic/engine.go index 5a8e808..3f1bab8 100644 --- a/internal/controllogic/engine.go +++ b/internal/controllogic/engine.go @@ -1053,11 +1053,7 @@ func (cg *compiledGraph) run(nodeID string, ctx *runCtx) { case "action.write": val := EvalValue(node.param("expr"), ctx.resolve) - f, isNum := val.(float64) - if !isNum { - f = math.NaN() - } - cg.emitDebug(node.ID, f, true) + cg.emitDebug(node.ID, val, true) cg.engine.write(cg, node.param("target"), val) cg.follow(node.ID, "out", ctx) diff --git a/internal/controllogic/engine_test.go b/internal/controllogic/engine_test.go index 4bda071..e43a148 100644 --- a/internal/controllogic/engine_test.go +++ b/internal/controllogic/engine_test.go @@ -110,3 +110,11 @@ func TestResolverReturnsLocalValue(t *testing.T) { t.Fatalf("sum(buf) = %v", v) } } + +func TestEmitDebugAcceptsArray(t *testing.T) { + g := Graph{ID: "g", Name: "n"} + cg := compile(g) + cg.engine = &Engine{} // no observer installed; emitDebug must not panic + cg.emitDebug("x", []Value{1.0, 2.0}, true) + cg.emitDebug("x", 3.0, true) +} diff --git a/internal/controllogic/lua.go b/internal/controllogic/lua.go index dad6673..f9aed97 100644 --- a/internal/controllogic/lua.go +++ b/internal/controllogic/lua.go @@ -1,6 +1,7 @@ package controllogic import ( + "math" "sync" lua "github.com/yuin/gopher-lua" @@ -60,9 +61,9 @@ func (lr *luaRuntime) ensure() error { L.SetGlobal("get", L.NewFunction(func(s *lua.LState) int { target := s.CheckString(1) ds, name, ok := parseRef(target) - var v float64 - if ok && lr.curResolve != nil { // shim: Task 6 finalizes lua Value handling - if f, fok := lr.curResolve(ds, name).(float64); fok { + v := math.NaN() + if ok && lr.curResolve != nil { + if f, isNum := lr.curResolve(ds, name).(float64); isNum { v = f } } diff --git a/internal/server/debughub.go b/internal/server/debughub.go index e0b62db..081d4ec 100644 --- a/internal/server/debughub.go +++ b/internal/server/debughub.go @@ -55,12 +55,12 @@ func NewDebugHub(engine *controllogic.Engine, log *slog.Logger) *DebugHub { // debugOut is the client-bound JSON form of a node-execution event. type debugOut struct { - Type string `json:"type"` // always "debugNode" - GraphID string `json:"graphId"` - NodeID string `json:"nodeId"` - Value float64 `json:"value"` - HasValue bool `json:"hasValue"` - TS int64 `json:"ts"` + Type string `json:"type"` // always "debugNode" + GraphID string `json:"graphId"` + NodeID string `json:"nodeId"` + Value any `json:"value"` + HasValue bool `json:"hasValue"` + TS int64 `json:"ts"` } // Observe implements controllogic.DebugObserver: push the event to every client