refactor(chat): marshal via the framework serializer, not hand-rolled JSON#5
refactor(chat): marshal via the framework serializer, not hand-rolled JSON#5nickmeinhold wants to merge 2 commits into
Conversation
Per Aiko's design principle (and Andy's note on geekscape#4): developers should express function calls and let the framework insert serialization + transport, rather than hand-writing the wire format. This swaps json.dumps/json.loads in chat.py for aiko_services' pluggable serializer. - generate_payload() now emits an Aiko function call via generate("message", {username, channel, timestamp, message}) -- an S-expression today, swappable to JSON/AVRO without touching this code. - format_incoming() decodes via parse(), falling back to legacy JSON and then a bare string, so older publishers keep working. NOTE (wire-format change): published messages move from JSON to S-expressions. Reads stay backward-compatible, but external consumers that parse the JSON directly -- notably the aiko-bridge -- need a matching parse() update as a follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three findings the adversarial reviewers (Maxwell/Kelvin/Carnot) agreed on: - Narrow `except Exception` to (ValueError, IndexError, TypeError) so real bugs surface instead of silently degrading to the JSON/raw fallback. - Hoist the magic command string "message" to a module constant _MESSAGE_COMMAND, used by both encode and decode paths. - Require the "message" field in the S-expr branch (mirroring the JSON branch) so a malformed (message username: nick) falls through to raw instead of rendering an empty "nick: " (Carnot's catch). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cage-match + companion bridge PRRun through adversarial review — Maxwell (Claude), Kelvin (Gemini 2.5 Pro), Carnot (Codex). Verdicts: 2× REQUEST_CHANGES + 1× COMMENT. Consensus findings, all fixed in
Wire-format follow-up — resolved: the consumer update for the aiko-bridge is up as nickmeinhold/aiko-bridge#1. It reads S-expr → legacy JSON → bare string, so it works against both old and new servers (no flag-day). The two land together. |
|
Noting a follow-up from the protocol version bump (dea848a, The aiko-bridge discovers the ChatServer via Tracking it bridge-side; not a change to this PR. Flagging here since it's the same wire-format thread. (Same direction as this PR, incidentally: the robot half of the bridge just moved off hand-rolled marshaling onto the framework's remote-call proxy, so commands are function calls and the framework handles serialization.) |
What & why
Andy's note on #4 named Aiko's core principle: developers design at the level of function calls and let the framework insert serialization + transport — they shouldn't hand-write the wire format.
chat.pywas doing exactly that withjson.dumps/json.loads.This swaps the hand-rolled marshalling for the framework's pluggable serializer (
aiko_services.main.utilities.generate/parse) — the same machinery the discovery proxy already uses for remote calls.Changes
generate_payload()→generate("message", {username, channel, timestamp, message}). Emits an S-expression today; swappable to JSON/AVRO without touching this code.format_incoming()→ decodes viaparse(), falling back to legacy JSON then a bare string, so older publishers keep working.Verified (round-trip + both fallbacks)
(message username: nick … message: 11:hello world)→nick: hello world{"username":"deanna","message":"hi"}→deanna: hijust text→just textPublished messages move from JSON to S-expressions. Reads here are backward-compatible, but external consumers that parse the JSON directly — notably the aiko-bridge — need a matching
parse()update. Flagging for sequencing; happy to land the bridge change alongside.Addresses Andy's #4 comment / the "let the framework do the marshalling" migration.
🤖 Generated with Claude Code