From 54581935a24378c84cd52f3407c533203339cf7b Mon Sep 17 00:00:00 2001 From: Zhongxuan Wang Date: Tue, 2 Jun 2026 13:01:43 -0700 Subject: [PATCH] docs: flush subscribers before deregister in package README examples The package READMEs' "Getting Started" examples register a subscriber, emit events, then deregister without calling flush. Native subscriber delivery is non-blocking (a background dispatcher), so examples that exit right after deregistering can lose queued events: - Python (`python/nemo_relay/README.md`): printed nothing, or intermittently a single partial line; the `to_dict()` / `to_json()` callback never ran. - Go (`go/nemo_relay/README.md`): flaky -- dropped the trailing tool-end and scope-end events on exit (verified: 2 of 3 runs were partial). Node and WASM are not currently broken (Node keeps the event loop alive via the ref'd ThreadsafeFunction until delivery; WASM's flush is a single-threaded no-op), but their READMEs are updated for parity so every package README models the same flush-before-deregister pattern the quickstarts already use. - Python / Node / WASM: call `flushSubscribers()` (`subscribers.flush()` in Python) before `deregister(...)`. - Go: `defer nemo.FlushSubscribers()`, ordered so it runs after the deferred `scope.Pop` emits the scope-end event and before the deferred deregister. Verified by running each example: with the flush, all events are delivered deterministically (Go 5/5, Python and Node 3/3 across repeated runs). Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: Zhongxuan Wang --- crates/node/README.md | 2 ++ crates/wasm/README.md | 2 ++ go/nemo_relay/README.md | 1 + python/nemo_relay/README.md | 5 +++++ 4 files changed, 10 insertions(+) diff --git a/crates/node/README.md b/crates/node/README.md index 319154b3..9a39b4db 100644 --- a/crates/node/README.md +++ b/crates/node/README.md @@ -66,6 +66,7 @@ const { ScopeType, deregisterSubscriber, event, + flushSubscribers, registerSubscriber, withScope, } = require("nemo-relay-node"); @@ -80,6 +81,7 @@ async function main() { event("initialized", handle, { binding: "node" }, null); }); + flushSubscribers(); deregisterSubscriber("printer"); } diff --git a/crates/wasm/README.md b/crates/wasm/README.md index 6040ca51..714d8521 100644 --- a/crates/wasm/README.md +++ b/crates/wasm/README.md @@ -83,6 +83,7 @@ const { ScopeType, deregisterSubscriber, event, + flushSubscribers, registerSubscriber, withScope, } = require("nemo-relay-wasm"); @@ -97,6 +98,7 @@ async function main() { event("initialized", handle, { binding: "wasm" }, null); }); + flushSubscribers(); deregisterSubscriber("printer"); } diff --git a/go/nemo_relay/README.md b/go/nemo_relay/README.md index 0d6b533d..314989f4 100644 --- a/go/nemo_relay/README.md +++ b/go/nemo_relay/README.md @@ -100,6 +100,7 @@ func main() { log.Fatal(err) } defer nemo.DeregisterSubscriber("printer") + defer nemo.FlushSubscribers() handle, err := scope.Push("demo-agent", nemo.ScopeTypeAgent) if err != nil { diff --git a/python/nemo_relay/README.md b/python/nemo_relay/README.md index 41dd930f..6f81c80c 100644 --- a/python/nemo_relay/README.md +++ b/python/nemo_relay/README.md @@ -142,9 +142,13 @@ nemo_relay.subscribers.register("printer", on_event) with nemo_relay.scope.scope("demo-agent", nemo_relay.ScopeType.Agent) as handle: nemo_relay.scope.event("initialized", handle=handle, data={"binding": "python"}) +nemo_relay.subscribers.flush() nemo_relay.subscribers.deregister("printer") ``` +Native subscriber delivery is asynchronous, so call +`nemo_relay.subscribers.flush()` before you read subscriber output or exit. + For host integrations that need a serialized event shape, consume the canonical JSON payload from the subscriber event object: @@ -164,6 +168,7 @@ try: with nemo_relay.scope.scope("demo-agent", nemo_relay.ScopeType.Agent): nemo_relay.scope.event("initialized", data={"binding": "python"}) finally: + nemo_relay.subscribers.flush() nemo_relay.subscribers.deregister("host-exporter") ```