Skip to content

fix: land grandcamel's four wire-shape fixes from an in-repo branch - #89

Merged
anthmFS merged 5 commits into
mainfrom
fix/grandcamel-wire-fixes
Aug 13, 2026
Merged

fix: land grandcamel's four wire-shape fixes from an in-repo branch#89
anthmFS merged 5 commits into
mainfrom
fix/grandcamel-wire-fixes

Conversation

@anthmFS

@anthmFS anthmFS commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Lands #84, #85, #86 and #87 from an in-repo branch so they can actually run CI, plus the one line worth keeping from #83.

Why re-homed rather than merged directly: those four are fork PRs, and fork
PRs do not receive repository secrets. Every job died in ~6s on
Input required and not supplied: token at the Checkout porting-sdk step,
which needs PORTING_SDK_TOKEN. They can never go green from a fork, whatever
is in them. Commits are cherry-picked with -x, so grandcamel stays the author
of all four.

The four, each verified against the authoritative source

#87execute_swml transfer rides beside the SWML document. Confirmed in
the engine: mod_openai/actions.c:129-140 reads transfer off item (the
action object), not off thing (the document). Inside the document it is never
read, so do_transfer stays 0 and execute_swml(..., transfer=True) does
nothing today. connect() and swml_transfer() already emit the sibling shape;
this was the outlier. Note actions.c also gates transfer on from_relay, so
the behaviour change only shows on relay-originated calls.

#86 — tap direction is speak/listen/both. The engine-generated SWML types say
direction: Literal["speak"] | Literal["listen"] | Literal["both"] and
codec: Literal["PCMU"] | Literal["PCMA"]. The SDK's runtime validator accepted
"hear" (which the engine rejects) and refused "listen" (the correct value).
It also omitted direction when it equalled the SDK default "both" — and the
SWML verb defaults to "speak" when absent, so tap(uri) asked for both and got
speak-only. Now always emitted.

#84conversation_timeout forwarded on the start path. prepare() puts it
in the params (gateway.py:451) and create_conversation() accepts timeout=,
but the HTTP route rebuilt the call and dropped it, so the browser was told 900
while the service kept its 3600 default.

#85 — live_transcribe docs + ai_sidecar test. The generated REST client takes
action: LiveTranscribeStartAction | … (object keyed by phase); the docs showed
action="start", lang="en". And ai_sidecar is in the bundled schema now, so
the test's raw-document bypass is obsolete — it goes through
add_verb_to_section and gets schema validation.

My two follow-up commits

The conversation_timeout test arrived unannotated, which reds TYPECHECK (mypy
has tests in scope on purpose). Annotated. And from __future__ import annotations in search_service.py is the one line salvaged from #83, which was
otherwise superseded by #81 — measured on its own it changes no mypy count, so
it is hygiene.

Verification

Full run-ci: everything passes except TYPECHECK and TEST, both at main's exact
baseline
— 19 mypy findings in mcp_gateway/search that depend on which optional
extras are installed and that CI does not report, and the 6 pre-existing
mcp_gateway test failures that fail identically on unmodified main. 5942 tests
pass (up 2, the new ones).

Heads-up: #86 and #87 change emitted wire shapes

Signatures barely move, so DRIFT will not notice, but EMISSION byte-compares
to_dict() and the 9 ports still emit the old shapes. This adds port work on top
of the ChatGateway drift. Both are fixing silently-broken behaviour, so the risk
is code that has been relying on the breakage — chiefly tap() now sending both
directions instead of speak-only.

Closes #84. Closes #85. Closes #86. Closes #87.

🤖 Generated with Claude Code

https://claude.ai/code/session_015dYktt85Ltj3oK9gG5VBww

grandcamel and others added 5 commits August 13, 2026 10:34
…_sidecar bypass

calling.md showed live_transcribe(call_id, action="start", lang="en"),
but the generated signature takes action as a keyword-only TypedDict
union ({"start": {...}} or the literal "stop") and has no lang/from_lang
/to_lang parameters — copying the documented call raises TypeError. The
examples now show the real shape, with the start parameters inside the
action object where the schema defines them.

The sidecar pattern test appended ai_sidecar to the raw document with a
comment that the verb was not in the live SWML schema yet. It landed in
4645d48, so the test now uses add_verb_to_section directly — the path
the comment promised, and stronger, since the verb goes through schema
validation instead of around it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgA3KeCEPMKMJVvroZY1wV
(cherry picked from commit 77fc34c)
prepare() puts conversation_timeout into the start params, but the HTTP
dispatch rebuilt the create_conversation call with only id and config_url,
silently dropping it. A gateway configured with conversation_timeout=900
told the browser 900 (via effective_timeout) while the service kept its
own 3600 default — the page schedules its idle warning around a number
the service never enforces. The chat path was unaffected (raw_post
streams params verbatim), which kept the drift invisible for
conversations opened by a first message.

The regression test drives the real HTTP dispatch through the ASGI
harness — prepare() was already correct, so a prepare()-level assertion
would have passed with the bug in place. Verified red without the fix,
green with it; it also pins the auto-create chat path so the two paths
cannot drift apart again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgA3KeCEPMKMJVvroZY1wV
(cherry picked from commit 5bf93c7)
execute_swml(transfer=True) wrote the flag INSIDE the SWML document —
{"SWML": {..., "transfer": "true"}} — where it is not a SWML key, so
the document executed but the call never exited the agent. The platform
documents transfer as a sibling of the SWML key in the action object,
which is exactly the shape the live-proven connect() and
swml_transfer() helpers already emit. The action is now
{"SWML": <doc>, "transfer": "true"}; transfer=False still omits the
key, and the caller's dict is still never mutated.

The three tests that pinned the inside placement now pin the sibling
placement, one of them asserting shape-parity with connect()'s action
so the two paths cannot drift apart again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgA3KeCEPMKMJVvroZY1wV
(cherry picked from commit 295c17e)
…mitted

The tap() helper disagreed with the SWML schema (and the platform docs)
twice over. It accepted and emitted "hear", which is not a tap
direction — the verb's enum is speak/listen/both, exactly the set
record_call() already uses — so direction="hear" produced SWML the
platform rejects; it now raises the existing ValueError instead of
silently emitting a dead tap. And its omit-if-default logic assumed the
verb's default matches the helper's "both" when the verb actually
defaults to "speak", so tap(uri) — documented as tapping both
directions — produced a speak-only tap. direction is now always
emitted; codec/rtp_ptime omissions stay (their helper defaults match
the verb defaults).

Note for the port audit: tap.direction's Literal feeds
python_signatures.json in porting-sdk as enum<...>, so the oracle needs
a regen alongside this change.

docs/swaig_reference.md and docs/api_reference.md updated to the real
enum (api_reference had a third variant — inbound/outbound plus a G722
codec and sip: URIs that the helper never accepted).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgA3KeCEPMKMJVvroZY1wV
(cherry picked from commit 0851a94)
Two small follow-ups to the four fixes cherry-picked above.

The `conversation_timeout` test arrived unannotated. mypy has `tests` in scope
deliberately ("a new untyped test fails the gate"), so it reds TYPECHECK on
main. Annotated; mypy is back to main's exact baseline.

`from __future__ import annotations` in search_service.py is the one line worth
keeping from #83, which was otherwise superseded by #81. Measured on its own: no
change to the mypy count, so it is hygiene rather than a fix.
@anthmFS
anthmFS merged commit 2d0c6c5 into main Aug 13, 2026
5 checks passed
@anthmFS
anthmFS deleted the fix/grandcamel-wire-fixes branch August 13, 2026 15:52
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.

2 participants