mirror of
https://github.com/logos-co/logos-cpp-sdk.git
synced 2026-08-27 15:51:10 +00:00
* fix(cdylib): bound the argument count above, not just below
An EXTRA argument was dropped and the call succeeded. The generated dispatch
guarded `args.size() < minArgs` and nothing bounded the other direction, on
every method at every arity -- measured on both providers of two different
contracts, through three consumer surfaces, as conformance case family
`failure/B/arity/too-many`.
Arity is the one part of a contract a caller cannot verify for itself. A
method that gains or loses a parameter upstream answered a stale caller with a
plausible value instead of a refusal, and the caller had no way to tell which
contract it had just talked to.
Two arms, not one, which is why the conformance table registered them as two
defects:
* The ordinary arm gets `args.size() > maxArgs`, where maxArgs is the
DECLARED parameter count rather than the required one -- bounding at
minArgs would reject a caller who legitimately supplies a trailing
optional. When the two coincide the message keeps the exact-count wording;
when they differ it says `at most`, because claiming a count the method
does not require would be wrong in the other direction.
* A ZERO-parameter method had no gate at all -- not the same guard with
minArgs = 0, a different code path, since `args.size() < 0` is unsigned and
was skipped as dead. The upper bound is emitted unconditionally, so it lands
here too.
The guard sits ABOVE the `md.derived` branch, so the generated identity
dispatch inherits it: `version("junk")` used to answer "1.0.0" with status ok,
which is worse than answering nothing -- a correct-looking reply to a call that
should have been refused.
Two existing tests asserted the old behaviour and are updated, not deleted:
`WrongArgumentCountReportsInvalidArgs` forbade any `args.size() >` in the
output, and `ZeroArgumentMethodEmitsNoArityGate` asserted a zero-arg method
emitted no `invalid_args` at all. That second assertion WAS the defect, written
down as a guarantee. Both now pin the bound, and two cases are added for the
arms they did not reach: a trailing optional widening the upper bound, and the
derived identity dispatch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(lp): a reply it cannot read must not become a provider refusal
The lp half of the same defect logos-qt-sdk fixes on the Qt consumer surface.
`jsonToStdResult` returned a default-constructed StdLogosResult for any
non-object reply, and that default -- `success = false`, empty error -- is
byte-for-byte what a provider sends when it REFUSES a call.
Every other lenient decode in this file bottoms out at a value no provider
means as an answer: "" for a string, 0 for a number, {} for a map. This one
did not. So "I could not read this reply" and "you were rejected" were the same
StdLogosResult, and no caller could separate them.
Fixed here as well as on the Qt side deliberately, and in the same change:
logos-qt-sdk's bare_scalar_slots_stay_lenient warns that tightening a scalar
decode on one surface without the other makes them diverge, and it is right.
The bare scalars stay lenient on both.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>