feat(I204/P3): child-side streamFrame routing → server-stream handler #2

Merged
buildagent merged 2 commits from feat/i204-p3-go-streamframe-routing into main 2026-05-30 15:28:21 +02:00
Member

Mission I204 / P3 (#182) — wires the Go child IPC loop to receive server-streams from the runtime. Pairs with the runtime side (ixt P0–P1.5: streamFrame @9 IPC type + Process relay + demuxing IPC).

  • Pin bump nested ixt-public 117319ca1ee954 (adds streamFrame @9 to IpcEnvelope.MessageType); sync-schemas.sh regenerates sdk/ipc/ipc.capnp.go (idempotent).
  • serialization.go: MessageTypeStreamFrame = 9 (+ String/Parse/capnp maps). Also fixed a latent preexisting bugkvChangeNotification was missing from the capnp↔Go MessageType maps (inbound kvChange envelopes mis-mapped to Handle).
  • module.go: connWriteMu (serializes main-loop responses with concurrent stream-handler writes), per-child stream table map[string]*StreamHandle, opt-in ServerStreamHandler (mirrors KvChangeHandler), sendRaw() funnel; read loop gains a streamFrame case (continue, no unary reply).
  • stream_dispatch.go (new): ServerStreamHandler { OnServerStream(*StreamHandle) error }; handleStreamFrame decodes via the I203 DecodeStreamFrame codec → Open → AcceptStream (decodes embedded InvocationContext) → register + spawn handler goroutine; Data/Credit → Feed; Close/Cancel/Error → Feed + drop. Open with no handler → terminal ErrStreamingUnsupported (no hang). Outbound streamFrameSink mirrors frames as streamFrame envelopes (same requestId).

Callee/server-stream ONLY — no module-initiated open. Reuses the I203 streaming.go wire codec + types.

Test plan

  • go build ./... + go test ./... green — 32 (+2): echo server-stream over net.Pipe (Open→3 ordered Data→mirrored echo→clean Close + table cleanup) + no-handler rejection
  • go test -race ./... clean (handler goroutine + main loop share the conn)
  • go vet + gofmt clean; sync-schemas.sh idempotent

Lockstep: superproject pin bumps after merge. (Other SDKs stay at 117319c — they don't participate in I204 Process streaming; the additive streamFrame @9 is harmless to them.)

🤖 Generated with Claude Code

Mission I204 / P3 (#182) — wires the Go child IPC loop to **receive server-streams** from the runtime. Pairs with the runtime side (ixt P0–P1.5: `streamFrame @9` IPC type + Process relay + demuxing IPC). - **Pin bump** nested `ixt-public` `117319c` → `a1ee954` (adds `streamFrame @9` to `IpcEnvelope.MessageType`); `sync-schemas.sh` regenerates `sdk/ipc/ipc.capnp.go` (idempotent). - `serialization.go`: `MessageTypeStreamFrame = 9` (+ String/Parse/capnp maps). **Also fixed a latent preexisting bug** — `kvChangeNotification` was missing from the capnp↔Go MessageType maps (inbound kvChange envelopes mis-mapped to `Handle`). - `module.go`: `connWriteMu` (serializes main-loop responses with concurrent stream-handler writes), per-child stream table `map[string]*StreamHandle`, opt-in `ServerStreamHandler` (mirrors `KvChangeHandler`), `sendRaw()` funnel; read loop gains a `streamFrame` case (`continue`, no unary reply). - `stream_dispatch.go` (new): `ServerStreamHandler { OnServerStream(*StreamHandle) error }`; `handleStreamFrame` decodes via the I203 `DecodeStreamFrame` codec → Open → `AcceptStream` (decodes embedded `InvocationContext`) → register + spawn handler goroutine; Data/Credit → `Feed`; Close/Cancel/Error → `Feed` + drop. Open with no handler → terminal `ErrStreamingUnsupported` (no hang). Outbound `streamFrameSink` mirrors frames as `streamFrame` envelopes (same `requestId`). **Callee/server-stream ONLY** — no module-initiated open. Reuses the I203 `streaming.go` wire codec + types. ## Test plan - [x] `go build ./...` + `go test ./...` green — 32 (+2): echo server-stream over `net.Pipe` (Open→3 ordered Data→mirrored echo→clean Close + table cleanup) + no-handler rejection - [x] `go test -race ./...` clean (handler goroutine + main loop share the conn) - [x] `go vet` + `gofmt` clean; `sync-schemas.sh` idempotent Lockstep: superproject pin bumps after merge. (Other SDKs stay at `117319c` — they don't participate in I204 Process streaming; the additive `streamFrame @9` is harmless to them.) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Bump nested ixt-public pin 117319c -> a1ee954, which adds the
streamFrame @9 ordinal to IpcEnvelope.MessageType in ipc.capnp.
Re-ran sync-schemas.sh to regenerate the Go ipc binding; the
generated IpcEnvelope_MessageType now includes streamFrame = 9.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire the Go module child IPC read-loop to receive server-streams over
IPC (callee-side only). The runtime delivers a server-stream as
streamFrame envelopes (IpcEnvelope.messageType == streamFrame @9,
requestId == StreamId, payload == ipc_streaming.capnp StreamFrame); the
module mirrors reply frames back the same way.

serialization.go:
  - add MessageTypeStreamFrame = 9 (+ String / ParseMessageType /
    to/fromCapnpMessageType). Also fill in the previously-missing
    kvChangeNotification cases in to/fromCapnpMessageType (inbound
    kvChange envelopes were silently mapped to Handle).

module.go:
  - ModuleRunner gains a connWriteMu (serialises main-loop responses
    with concurrent stream-handler writes), a per-child stream table
    (map[StreamID.String()]*StreamHandle guarded by streamsMu), and an
    optional streamHandler wired via the ServerStreamHandler opt-in
    (mirrors the KvChangeHandler pattern).
  - new sendRaw() funnels every conn write through connWriteMu;
    sendResponse and SendServiceRequest now use it.
  - read loop gains a streamFrame case that routes to handleStreamFrame
    and continues (NOT request/response, so no unary reply).

stream_dispatch.go (new):
  - ServerStreamHandler interface: OnServerStream(*StreamHandle) error.
  - streamFrameSink: FrameSink that wraps encoded StreamFrames in a
    streamFrame envelope with requestId == stream id and writes via
    sendRaw.
  - handleStreamFrame decodes the frame (reusing the I203 DecodeStreamFrame
    codec) and routes: Open -> AcceptStream + register + spawn handler;
    Data/Credit -> handle.Feed (ordered, single read loop); terminal
    (Close/Cancel/Error) -> Feed + drop table entry. Open with no
    registered handler is rejected with a terminal StreamingUnsupported
    error so callers do not hang.

stream_dispatch_test.go (new): drives the child loop over a net.Pipe
without the real runtime — echo server-stream (Open -> 3 ordered Data
-> mirrored Data in order -> Close -> mirrored clean Close + table
cleanup), plus a no-handler rejection test. Race-clean.

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-go!2
No description provided.