Skip to content

Reject answers with missing, absent, or mistyped values instead of reading them as zero - #3

Merged
garretpremo merged 3 commits into
Premo-Cloud:mainfrom
xbt-a4224j:fix/incomplete-answers
Sep 19, 2026
Merged

garretpremo merged 3 commits into
Premo-Cloud:mainfrom
xbt-a4224j:fix/incomplete-answers

Conversation

@xbt-a4224j

@xbt-a4224j xbt-a4224j commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Motivation:
Two defects from #1, both letting a malformed response pass as a good one.

  1. A missing answer value reads as 0.0, since the record components are primitives. {"is_fraud":{"type":"noul"}} parses and noul("is_fraud") returns 0.0.
  2. An absent or mistyped answer only fails when the caller reads that key, as IllegalArgumentException, so catch (TypeSafeException e) misses it.

Changes:

  • Answer records: @JsonCreator over boxed values, rejecting missing or null required fields. legend stays optional.
  • deserialize: include getPathReference() so the message names the question.
  • systemOne: diff the question ids sent against the answers returned, and compare each answer's type to its question's.
  • systemOneKeepsAnExplicitModel asked q against a stub answering other ids; fixture now matches.

Result:
6 new tests, all failing on main with "nothing was thrown". 35 pass. Both defects now throw TypeSafeException from systemOne:

Could not read response at answers -> is_fraud: ... noul answer is missing 'noul'

Verified against live jev-1.13.0. Matches the Python SDK, which declares these fields required under strict=True.

Addresses: #1

…g them as zero

A noul answer with no value deserialized as 0.0, a choice with no choice as
null, and so on, because the record components are primitives and Jackson
defaults a missing primitive. In a fraud check that reads as "not fraud".

Each answer record now has a JsonCreator taking boxed values and rejecting a
missing or null required field, so the existing parse error path turns it into
a TypeSafeException from systemOne. Parse errors also carry the JSON path so
the message names the question (answers -> is_fraud).

Fixes #1 (first half; the accessor exception type is left as is).
A question with no answer in the response only surfaced when the caller read
that key, as an IllegalArgumentException, so systemOne returned normally and a
catch of TypeSafeException never saw it.

systemOne now diffs the question ids it sent against the answers it got back
and throws TypeSafeException naming the unanswered ones, next to the existing
null-answers check.

One existing test asked question 'q' while the canned stub answered
is_phishing/spam_category/urgency; its subject is the request's model field, so
the question id now matches the fixture.
A noul question answered with a choice parsed fine and only failed when the
caller read it, as an IllegalArgumentException indistinguishable from using the
wrong accessor by mistake. The request knows what each question was, so the
mismatch is detectable where the response is checked.

systemOne now compares each question's type against its answer's and throws
TypeSafeException naming the mismatched ids. Reading a good answer with the
wrong accessor is still IllegalArgumentException: that is a caller bug, not a
bad response.
@garretpremo
garretpremo merged commit b96540c into Premo-Cloud:main Sep 19, 2026
1 check passed
@garretpremo garretpremo added the bug Something isn't working label Sep 19, 2026
garretpremo added a commit that referenced this pull request Sep 19, 2026
A response missing an answer, an answer field, or answering with a
different type than asked now fails systemOne with a TypeSafeException
instead of reading as zero or surfacing later (#1, #3).
RequestOptions.maxRetries applies on top of the client's policy instead
of replacing it with the default (#2). READMEs point at 0.2.0.
@xbt-a4224j
xbt-a4224j deleted the fix/incomplete-answers branch September 20, 2026 01:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants