feat(I203/M1): InvocationContext + streaming parity (post-I198/I199) #1

Merged
buildagent merged 2 commits from feat/i203-m1-streaming into main 2026-05-29 16:44:17 +02:00
Member

Summary

Mission I203 / M1 — brings the Python SDK to parity with the Rust SDK for InvocationContext (spec 21) and streaming (spec 23). Closes the 13-commit nested-ixt-public lag (eaf4205117319c).

Phase A — post-I198 migration

  • Bump nested ixt-public pin eaf4205117319c37e78 (adds ipc_streaming.capnp, InvocationContext on module.capnp, observability/storage).
  • New ixt_sdk/invocation.py: ActorSubject / ActorIdentity (capabilities + opaque token_ref, wildcard-aware) / MessageTarget / InvocationContext with capnp to_bytes/from_bytes. Replaces the deleted SecurityLevel with metadata-key conventions (tenant / auth.strength / ingress.*), mirroring Rust types.rs+conversions.rs. (The Python SDK previously passed an untyped context dict — migration was additive, no deletions.)
  • Loader (serialization.py) extended to load module.capnp; module.py::_handle_message decodes HandlePayload.context into a typed InvocationContext.

Phase B — streaming API

  • Loader also loads ipc_streaming.capnp.
  • New ixt_sdk/streaming.py mirroring streaming.rs+stream_wire.rs: 4 InteractionKinds, CloseReason/CancelReason/13-variant StreamErrorCode, StreamId/StreamData/StreamError, 6-variant StreamFrame union with capnp round-trip, StreamSender (send_data/close/cancel/error + spec-23 credit/backpressure, additive grant, monotonic sequence), StreamHandle (accept-side, decodes the Open frame's InvocationContext). No bespoke flow control beyond the credit model.

Test plan

  • 55 pytest pass (20 existing + 35 new) — all 6 StreamFrame variants + all InvocationContext target/subject variants round-trip
  • Wire bytes byte-for-byte identical to the Rust stream_wire.rs test vectors (golden hex fixtures pin this — canonical single-segment Cap'n Proto)
  • Package imports cleanly against the bumped schemas

Lockstep: superproject submodule pin bumps after merge. Schema-pin drift (this SDK) clears.

🤖 Generated with Claude Code

## Summary Mission I203 / M1 — brings the Python SDK to parity with the Rust SDK for **InvocationContext** (spec 21) and **streaming** (spec 23). Closes the 13-commit nested-`ixt-public` lag (`eaf4205` → `117319c`). ## Phase A — post-I198 migration - Bump nested `ixt-public` pin `eaf4205` → `117319c37e78` (adds `ipc_streaming.capnp`, InvocationContext on `module.capnp`, observability/storage). - New `ixt_sdk/invocation.py`: `ActorSubject` / `ActorIdentity` (capabilities + opaque `token_ref`, wildcard-aware) / `MessageTarget` / `InvocationContext` with capnp `to_bytes`/`from_bytes`. Replaces the deleted `SecurityLevel` with metadata-key conventions (tenant / auth.strength / ingress.*), mirroring Rust `types.rs`+`conversions.rs`. (The Python SDK previously passed an untyped `context` dict — migration was additive, no deletions.) - Loader (`serialization.py`) extended to load `module.capnp`; `module.py::_handle_message` decodes `HandlePayload.context` into a typed `InvocationContext`. ## Phase B — streaming API - Loader also loads `ipc_streaming.capnp`. - New `ixt_sdk/streaming.py` mirroring `streaming.rs`+`stream_wire.rs`: 4 `InteractionKind`s, `CloseReason`/`CancelReason`/13-variant `StreamErrorCode`, `StreamId`/`StreamData`/`StreamError`, 6-variant `StreamFrame` union with capnp round-trip, `StreamSender` (send_data/close/cancel/error + spec-23 credit/backpressure, additive grant, monotonic sequence), `StreamHandle` (accept-side, decodes the Open frame's InvocationContext). No bespoke flow control beyond the credit model. ## Test plan - [x] 55 pytest pass (20 existing + 35 new) — all 6 StreamFrame variants + all InvocationContext target/subject variants round-trip - [x] **Wire bytes byte-for-byte identical to the Rust `stream_wire.rs` test vectors** (golden hex fixtures pin this — canonical single-segment Cap'n Proto) - [x] Package imports cleanly against the bumped schemas Lockstep: superproject submodule pin bumps after merge. Schema-pin drift (this SDK) clears. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Bump the nested ixt-public schema submodule from eaf4205 to 117319c
(post-I198+I199 era: adds ipc_streaming.capnp, InvocationContext on
module.capnp, kvChangeNotification on ipc.capnp).

Add the spec-21 InvocationContext model (ixt_sdk/invocation.py),
mirroring sdk/ixt-sdk-rust types.rs/conversions.rs:
- ActorSubject (system/module/operator/external tagged union)
- ActorIdentity (subject + opaque token_ref + capability set, wildcard-aware
  has_capability)
- MessageTarget (service/module/broadcast/reply/dynamicEndpoint union)
- InvocationContext with Cap'n Proto to_bytes/from_bytes round-trip against
  module.capnp, anonymous() constructor, deadline_elapsed(), metadata_keys
  (tenant / auth.strength / ingress.kind / ingress.source_ip) — replacing the
  deleted SecurityContext/SecurityLevel.

Extend the schema loader (serialization.py) to also load module.capnp,
exposed via IpcSerializer.module_schema. Wire _handle_message in module.py to
decode HandlePayload.context into a typed InvocationContext, exposed to
handlers under context['invocation'] plus lifted flat fields
(correlation_id/span_id/source_module/actor/capabilities/deadline_ms/metadata).

Verification: existing 20 serialization tests pass unchanged; the package
imports cleanly against the bumped schemas; InvocationContext round-trips all
target/subject variants.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extend the schema loader to also load ipc_streaming.capnp (exposed via
IpcSerializer.streaming_schema).

Add ixt_sdk/streaming.py mirroring sdk/ixt-sdk-rust streaming.rs +
stream_wire.rs (spec 23 / Mission I199), minimal-but-complete:
- Enums: InteractionKind (unary/clientStream/serverStream/bidiStream — wire
  ordinals preserved), CloseReason, CancelReason, StreamErrorCode (string
  values match the capnp field names).
- StreamId (16-byte UUID, hex Display), StreamData, StreamError.
- StreamFrame tagged union (open/data/error/close/cancel/credit) with
  to_bytes/from_bytes Cap'n Proto round-trip against ipc_streaming.capnp.
- StreamSender (callee send half): send_data/close/cancel/error with the
  spec-23 credit/backpressure model (one credit per data frame, grant_credit
  additive, async wait on exhaustion, monotonic sequence). No bespoke flow
  control beyond the credit model.
- StreamHandle (accepted-stream handle): stream_id/interaction/sender +
  opaque InvocationContext bytes from the Open frame (invocation_context()
  decodes them); from_open() builds it from an inbound Open + frame sink.

Tests (tests/test_streaming.py, 35 cases): StreamFrame round-trips for all 6
variants, InvocationContext round-trips for all target/subject variants,
StreamSender credit/backpressure/close/cancel/error, StreamHandle.from_open.
Golden hex fixtures use the SAME inputs as the Rust SDK's stream_wire.rs tests
(stream_id=[1..16], StreamData::new(7,"sdk-data"), etc.) — both SDKs emit
canonical single-segment Cap'n Proto, so the wire bytes are byte-for-byte
identical across languages; the fixtures pin that.

Verification: full suite 55 passed (20 existing + 35 new); package imports
cleanly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
h-dv/ixt-sdk-python!1
No description provided.