A type assertion only tells the compiler what to believe. An external response starts as bytes and says nothing about its variant until something checks it.
Text decoding and JSON parsing come first, each with its own failure path. The parsed value should reach validation as unknown.
The supported response is either a ready tag with an items array of strings or a failed tag with a message string. The runtime check has to confirm three things. The value is an object, its tag is one we know, and the payload for that tag has the required shape. An object with ready and no items is incomplete. One with waiting carries a tag outside the union. Writing as with the union fixes neither.
Once the check passes, the decoder returns a discriminated union. Testing for ready narrows it, and the items become usable. An exhaustive check with never points out an unhandled alternative in the declared model. Useful for maintenance, but it runs on types, not on response bytes.
Rejected input still needs a decision. Report a decoding failure, or model unknown responses as a variant on purpose. Casting straight into the accepted union skips that choice.
Keep the runtime evidence right next to where the typed value gets built.