SWML: fix shipped doc defects and document messaging transfer/execute dest forms (#510) - #511
Draft
Devon-White wants to merge 3 commits into
Draft
SWML: fix shipped doc defects and document messaging transfer/execute dest forms (#510)#511Devon-White wants to merge 3 commits into
Devon-White wants to merge 3 commits into
Conversation
… dest forms Contains two groups of changes. One is correct against shipped behavior today; the other describes behavior that has not shipped yet. See the gating note below. ## Shipped-behavior fixes (correct today) Voice reference: - transfer.mdx: delete the array-of-objects example. It fails schema validation and rejects the entire document, so copying it broke the customer's script. - transfer.mdx: remove the step placed after a transfer. transfer is a tail call; that step never ran. Tail-call behavior is now stated outright. - transfer.mdx + TypeSpec: drop the inline-SWML-document claim. Voice transfer accepts a URL or section name only and never accepted inline documents. - execute.mdx + TypeSpec: correct an inverted rule. An array result block runs as cond and an object runs as switch, not the reverse. Document the previously omitted inline YAML and bare-instruction-array forms. - goto.mdx + TypeSpec: document the shared jump budget and per-label max counting. - Document the bare-string shorthand for transfer, execute, and goto. Messaging reference: - overview.mdx and variables.mdx: remove the vars.* variable scope, which does not exist in messaging. Runtime variables are read without a prefix; a vars.-prefixed path silently expanded to an empty string in the customer's outgoing message. vars remains documented as what it actually is: a field of the outbound webhook payload, not an interpolation scope. - overview.mdx: runtime variables are delivered in the webhook payload but the transferred document's context is rebuilt, so drop the propagation claim. - overview.mdx and request.mdx: request_response_code is a string, not an integer. - overview.mdx: replace the unenforced content-type requirement with the actual parse behavior. - errors.mdx: correct two wrong error-code strings, add missing codes, fix message drift, and group entries by document, method, and runtime level. enter_queue and connect: rename transfer_after_bridge to execute_after_queue, renamed on the backend on 2026-06-01. connect validates against a closed parameter whitelist, so the worked example rejected the whole document. On enter_queue the old name was silently ignored and the post-call SWML never ran. Also drop the required flag, since the same change made it optional and defaults to hangup, and remove the parameter from the parallel, serial, and serial_parallel forms, which have no working placement for it. Regenerates fern/apis/signalwire-rest/openapi.yaml, which is emitted from these SWML models. Changes are confined to SWML.Calling.* and SWML.Messaging.* schemas. ## docs#510 - BLOCKED, do not merge yet Documents messaging transfer and execute accepting a section name, a URL, or an inline SWML document, plus the swml_execute_fetch_failed and swml_execute_document_invalid error codes. This behavior exists only in signalwire/prime-rails#9574, which is OPEN and UNMERGED (upstream signalwire/cloud-product#18950). Hold this PR as a draft until that merges and deploys, then re-verify against the merge commit, since the PR can still change in review. Note that issue #510's premise that these are "all three forms that Voice SWML already supports" is not accurate. Voice transfer never accepted inline documents, so after #9574 messaging transfer is a superset of voice on destination forms while being narrower on guardrails. The docs avoid claiming parity in either direction. Verified: yarn build:schema compiles both SWML projects; yarn fern-md-check reports all 2810 MDX files valid.
Contributor
Four independent reviewers checked the previous commit against source. The riskiest edits held up: removing inline support from voice transfer, and removing execute_after_queue from three connect forms, were both confirmed. These are the claims that did not. Messaging destination context (the largest error, in five places). Both method pages, their TypeSpec, and the messaging overview claimed a URL or inline destination starts from a fresh context of message plus params, and that it does not inherit the caller's runtime variables. That asymmetry does not exist: in the ordinary case all three destination forms read the same variables. Where a real difference occurs it runs the other way, with a URL or inline destination seeing a subset. The per-form table is replaced with prose stating what a destination does read, keeping the one genuine exception: a step nested inside a destination called by execute or transfer does not see variables set inside that call. Voice execute inline forms. "A JSON or YAML string ... or just an array of instructions" implied four combinations, one of which does not exist. A bare instruction array is JSON only; a YAML string must be a full document. Error descriptions. swml_invalid_message_handler cited a LaML bin, which is the one case that provably cannot produce it; the real trigger is a Call Flow or AI Agent handler. url_failed_to_parse also fires on any non-2xx response, which is its most common cause. http_retrieval_error covers connection and timeout failures only. message_filtered does not mark the message failed and is not carrier filtering, and the section intro no longer over-claims for it. relay_script_element_invalid_value and relay_script_section_code_missing had stated triggers that did not match their only raise sites. Content types are not enforced on either flavor. Both determine the format from the response body, so the three types are now good practice rather than a requirement. The calling overview also listed join_conference.wait_url and enter_queue.wait_url as SWML document fetches; the first is a LaML playlist and the second an audio file, so both are removed from that list. Also: the vars webhook field carries return_value once an earlier execute sets one, and variables.mdx now states the scopes per flavor instead of merging two different sets into one list. Verified: yarn build:schema compiles both SWML projects; yarn fern-md-check reports all 2810 MDX files valid.
Re-verification of the previous commit found the corrected sentence was itself
an approximation. It said a nested step's URL or inline destination sees the
variables accumulated before the enclosing call. That is right only one level
deep. A URL or inline destination reads the runtime variables of the top-level
flow at any depth, because variables set inside a destination that execute or
transfer entered are not visible until control returns to the top level. Nesting
two levels deep is reachable in ordinary documents, so the sentence over-credited
the destination with variables it cannot read.
The wording now separates the two cases rather than stating one rule with an
exception: a section destination reads everything the caller can, while a URL or
inline destination reads the top-level flow's variables, which are the same thing
whenever the step sits in main. Applied to both method pages, the messaging
overview, and both TypeSpec doc strings, with the emitted schema regenerated.
Also on the messaging overview: add return_value to the runtime variables, since
it is readable as %{return_value} and is named in the transfer webhook payload
list, and drop the note saying methods may write additional keys. The variable
set is closed at seven names, verified across every write site, so the note
contradicted the enumeration.
The error-reference corrections from the previous commit were re-verified
independently against their raise sites and need no further change.
Verified: yarn build:schema compiles both SWML projects; yarn fern-md-check
reports all 2810 MDX files valid.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Correction to the premise of #510
Issue #510 describes these as "all three forms that Voice SWML already supports." That is
not accurate, and the docs here deliberately avoid repeating it:
transfernever accepted inline documents. Verified by enumerating every callsite of the fetch and parse paths in the C source; none is reachable from the transfer
handler. Our voice
transferpage and TypeSpec had been claiming inline support thatnever existed.
executeaccepts more than we documented — inline may be YAML as well as JSON,and may be a bare instruction array.
So after #9574, messaging
transferis a superset of voice on destination forms, whilebeing narrower on guardrails (no size cap, no YAML or array inline, no
meta). Neither"parity" nor "matches voice" is stated in either direction.
Shipped-behavior fixes (not gated)
These were found while auditing #510 and are live defects in published docs.
Two broken examples on the voice
transferpage. One documented an array-of-objectsform that fails schema validation and rejects the entire document — copying it broke the
reader's script. The other placed a step after a
transfer, implying execution returns;transferis a tail call and that step never ran.A variable scope that does not exist in messaging.
messaging/overview.mdxandvariables.mdxdocumented runtime variables undervars.*, including a copy-pasteable%{vars.request_response.field_name}. Messaging reads those without a prefix, so thedocumented form expanded to an empty string — a silent wrong value in the customer's
outgoing message, with no error.
varsis real in messaging, but as a field of theoutbound webhook payload rather than an interpolation scope; that distinction is now
explicit so the prefix is not restored later. Voice pages are untouched: voice strips a
vars.prefix on read, sovars.*is correct there.A parameter renamed on the backend two months ago.
transfer_after_bridgebecameexecute_after_queueon 2026-06-01.connectvalidates against a closed whitelist, so itsworked example rejected the whole document; on
enter_queuethe old name was silentlyignored and the post-call SWML never ran. Also dropped the
requiredflag (the same changemade it optional, defaulting to hangup) and removed the parameter from the
parallel,serial, andserial_parallelforms, which have no working placement for it.Error reference. Two wrong code strings corrected, missing codes added, message drift
fixed, and entries grouped by document, method, and runtime level.
Also: an inverted rule on voice
execute(an array result block runs ascond, anobject as
switch— the page said the reverse), the previously undocumented bare-stringshorthand, the shared jump budget on
goto, and several broken anchors.Verification
yarn build:schema— both SWML projects compile cleanyarn fern-md-check— all 2810 MDX files valid