Skip to content

Client graph verbs: add_input and add_data_join (#30) - #31

Merged
andre-merzky merged 4 commits into
develfrom
feature/client-graph-verbs
Aug 30, 2026
Merged

Client graph verbs: add_input and add_data_join (#30)#31
andre-merzky merged 4 commits into
develfrom
feature/client-graph-verbs

Conversation

@andre-merzky

Copy link
Copy Markdown
Member

The two verbs the servicified demo actually calls (run_me_service.py in radical-collaboration/amsc#5): add_input binds an external channel to an input dtype (data-only wire: dtype, channel, codec — the producer lives outside the framework), add_data_join registers a join. Session side is passthrough to the runtime, which already validates and subscribes at bind time.

Second commit fixes a crash this surfaced: ChannelPublisher.open() defaults to timeout=None ("wait forever"), which the orbit stream backend forwarded into ORBIT's start(timeout: float)monotonic() + None TypeError. Every external producer on the orbit data plane with the default timeout hit it; None is now approximated by a day. The demo's sensors use exactly that default.

Tests: 3 unit (binding lands, channel refusal surfaces as 409, join registers) + 1 integration running the demo shape end to end (two ChannelPublishers → add_input ×2 → add_data_joinJoinSink, complete pairs in order). Local: 175 unit + 30 integration passed.

Out of scope, staying open on #30: add_barrier / add_data_split_task — no consumer yet, and the barrier verb needs a wire decision (a Barrier instance holds asyncio primitives and must not cross; a declarative spec should).

🤖 Generated with Claude Code

https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU

andre-merzky and others added 4 commits August 30, 2026 22:31
The runtime had both; the service client had neither, which is what
blocks the servicified demo.  Two new graph verbs, passthrough on the
session side: `add_input` carries data only -- dtype, channel, codec --
because the producer lives outside the framework, and `add_data_join`
carries the joined dtype.  Unit tests cover the binding, the runtime's
channel refusal surfacing as 409, and join registration.

`add_barrier` / `add_data_split_task` from #30 stay open: no consumer
exists yet, and the barrier verb carries a wire-design decision
(instances hold asyncio primitives and must not cross; a declarative
spec should).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
`ChannelPublisher.open()` defaults to `timeout=None` -- the streaming
contract's "wait forever" -- but the orbit backend forwarded that None
into ORBIT's `start(timeout: float)`, which dies on `monotonic() +
None`.  Every external producer on the orbit data plane with the
default timeout hit this.  None is now approximated by a day.

The new integration test runs the servicified demo shape end to end:
two external channels bound with `add_input`, joined with
`add_data_join`, consumed by a `JoinSink` -- producers are plain
`ChannelPublisher`s using exactly that default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
Three review findings applied:

- `runtime.add_input` returns its subscription task and the service
  verb awaits it, so the client's success means the binding is live --
  a producer publishing right after the call no longer races the broker
  registration, and a subscribe failure reaches the caller instead of
  only a server-side log.
- a re-bind that changes the codec is refused (ValueError -> 409): the
  stream client dedupes on (channel, dtype), so the change would be
  recorded in `describe()` yet never applied.
- `ChannelPublisher.open` defaults to CLIENT_CONNECT_TIMEOUT like every
  other orbit connect path -- an external producer pointed at an
  unreachable broker fails in 30s instead of holding an uncancellable
  thread for the day-long None approximation, which stays as the
  backstop for an explicit None (and its timeout message now names the
  effective deadline, not "None").

Plus: the client docstring states the stream's real delivery contract,
the join test asserts against `join_components` directly, and the None
substitution is pinned at unit level.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
- the add_input unit test drops its settling sleep -- the verb answers
  only once the subscription is live, and the test now pins exactly
  that (with the sleep, a regression back to fire-and-forget would
  still pass)
- local imports hoisted to the module blocks they belong in
- the integration helper loses a parameter nothing passed
- `add_input` carries its new return contract in the signature
- a dead fake attribute removed

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
@andre-merzky
andre-merzky merged commit cfa4c9a into devel Aug 30, 2026
2 checks passed
@andre-merzky
andre-merzky deleted the feature/client-graph-verbs branch August 30, 2026 21:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant