Delay node: hold the latest value inside the interval and send it at the window's end

The interval now rides the output ports, so the pipeline's own hold and flush
keep what arrives inside the window instead of the node dropping it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WgBfkm4rCmtd1T4vHWjEPx
This commit is contained in:
2026-10-15 09:32:35 +02:00
co-authored by Claude Opus 5.5
parent 7d28b44451
commit afcbb63a56
3 changed files with 45 additions and 18 deletions
+28 -1
View File
@@ -4,7 +4,7 @@ import time
from fluksio.flow.executor import ExecutionService
from fluksio.flow.messages import DType, MessageSpec
from fluksio.flow.nodes import Node
from fluksio.flow.nodes import DelayNode, Node
from fluksio.flow.pipeline import Pipeline
from fluksio.flow.queue import MemoryWorkQueue
@@ -213,6 +213,33 @@ def test_a_node_whose_inputs_did_not_change_is_not_run():
assert seen == [1.0, 2.0]
def test_a_delay_interval_sends_the_latest_value_when_the_window_ends():
"""It used to drop a value inside the window rather than hold it."""
seen: list[float] = []
source = make_node("source", lambda params: None, provides=[spec("raw")])
delay = DelayNode(
params={"interval": WINDOW},
requires=[spec("raw")],
provides=[spec("out")],
name="delay",
)
delay.assign_flow("demo", "delay")
watcher = make_node(
"watcher", lambda out, params: seen.append(out), requires=[spec("out")]
)
_pipeline, queue, service = _running([source, delay, watcher])
for value in (1.0, 2.0, 3.0):
source.inject({"raw": value})
for item in queue.claim(10, 10):
service._run_item(item)
assert seen == [1.0]
time.sleep(WINDOW * 2)
assert _run_due(queue, service) == 1
assert seen == [1.0, 3.0]
def test_a_join_behind_a_skipped_branch_still_runs():
"""Skipping a node frees its consumers, which may already have been passed."""
seen: list[tuple[float, float]] = []