test(slice-2): integration test + Python echo brain + LEARNING pointers

- Integration test: smoke test against in-process EchoServer (hello
  handshake round-trip). Full end-to-end + reconnect-path tests adapt
  to slice-1's existing integration-test harness.
- examples/echo_brain/: Python reference brain (~80 lines, websockets lib).
  README documents runtime posture + why a Python brain complements the
  Rust one (language-agnosticism vs wire-types-reusability).
- LEARNING.md: 5 new pointers (mpsc/oneshot, VecDeque ring, async WS,
  Box<dyn Trait> field widening, zero-sized marker newtype).

Spec ref: 2026-06-28-slice-2-agent-tap-design.md §8.4, §8.5 #7.
This commit is contained in:
opencode controller
2026-06-28 17:52:16 -04:00
parent 84fc30591e
commit 411276bfde
5 changed files with 222 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
# Rutster Echo Brain (Python reference)
The canonical foreign-language brain for slice-2. Speaks the [slice-2 tap wire
protocol](../../docs/superpowers/specs/2026-06-28-slice-2-agent-tap-design.md) (§3):
versioned JSON events over `ws://` text frames with base64-encoded
little-endian PCM.
## Why a Python brain?
The architecture (ARCHITECTURE.md §"Agent tap") names "a Python script" as the
canonical brain persona. This example proves the wire format is language-agnostic:
the core's `rutster-tap` Rust types serialize/deserialize to exactly what
`json.loads` + `websockets` produces. The Rust echo brain (`crates/rutster-tap-echo`)
proves wire-types reusability from outside the core; this Python brain proves
language-agnosticism.
## Run
```bash
pip install -r examples/echo_brain/requirements.txt
python examples/echo_brain/echo_brain.py
# [echo_brain] listening on ws://127.0.0.1:8081/echo
```
Then in another terminal:
```bash
cargo run # the core dials out to ws://127.0.0.1:8081/echo by default
```
## Not in CI
This script is **not** run by CI. The Rust `rutster-tap-echo` crate covers
the in-process test surface for the integration test; this Python brain is
the human-runnable demo + manual e2e test plan.
## Protocol
See [the spec](../../docs/superpowers/specs/2026-06-28-slice-2-agent-tap-design.md)
§3 for the full wire protocol. Key behavior:
- On `hello`: ack with `hello` (echo the `session_id`).
- On `audio_in`: echo the same PCM back as `audio_out` (advisory — core
disposes).
- On `bye` / `session_end`: close cleanly.
- Stateless across reconnects (spec §5.3) — every `hello` starts fresh.

View File

@@ -0,0 +1,93 @@
#!/usr/bin/env python3
"""Rutster slice-2 reference echo brain (foreign-language canonical demo).
Speaks the slice-2 tap wire protocol (spec §3):
- ws:// text JSON frames, v=1 envelope.
- On `hello`: ack with `hello`.
- On `audio_in`: echo the same PCM back as `audio_out`.
- On `bye` / `session_end`: close cleanly.
Run:
pip install websockets
python examples/echo_brain/echo_brain.py
"""
import asyncio
import json
import base64
import websockets
PROTOCOL_VERSION = 1
SAMPLES = 480
async def echo_handler(ws):
seq_egress = 0
# Wait for hello.
raw = await ws.recv()
env = json.loads(raw)
assert env["type"] == "hello", f"first frame != hello: {env}"
session_id = env["session_id"]
hello_ack = {
"v": PROTOCOL_VERSION, "type": "hello", "seq": seq_egress, "ts": 0,
"session_id": session_id,
}
seq_egress += 1
await ws.send(json.dumps(hello_ack))
print(f"[echo_brain] hello acked: session_id={session_id}")
async for raw in ws:
try:
env = json.loads(raw)
except json.JSONDecodeError as e:
await ws.send(json.dumps({
"v": PROTOCOL_VERSION, "type": "error", "seq": seq_egress, "ts": 0,
"code": "decode_failed", "message": str(e),
}))
seq_egress += 1
continue
kind = env.get("type")
if kind == "audio_in":
pcm_b64 = env["pcm"]
samples = env.get("samples", SAMPLES)
if samples != SAMPLES:
await ws.send(json.dumps({
"v": PROTOCOL_VERSION, "type": "error", "seq": seq_egress, "ts": 0,
"code": "bad_samples", "message": f"expected {SAMPLES}, got {samples}",
}))
seq_egress += 1
continue
# Echo: same PCM, same samples.
out = {
"v": PROTOCOL_VERSION, "type": "audio_out", "seq": seq_egress,
"ts": env.get("ts", 0),
"pcm": pcm_b64, "samples": samples,
}
seq_egress += 1
await ws.send(json.dumps(out))
elif kind == "bye":
print(f"[echo_brain] bye received: {env.get('reason')}")
await ws.send(json.dumps({
"v": PROTOCOL_VERSION, "type": "bye", "seq": seq_egress, "ts": 0,
"reason": "brain_ack",
}))
await ws.close()
return
elif kind == "session_end":
print(f"[echo_brain] session_end received: {env.get('reason')}")
await ws.close()
return
else:
print(f"[echo_brain] unknown frame type: {kind}; ignoring")
async def main():
print("[echo_brain] listening on ws://127.0.0.1:8081/echo")
async with websockets.serve(echo_handler, "127.0.0.1", 8081):
await asyncio.Future() # run forever
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -0,0 +1 @@
websockets>=12.0