Files
logos-test-modules/test-fullapi-ext-module-cpp
Dario LipicarandClaude Opus 5 a969c20e96 test(conformance): the LIDL optional contract, as cases, before the implementation (#37)
* test(conformance): the LIDL optional contract, as cases, before the implementation

`?T` is declared by every contract and read by no generator, in any language.
This writes down what it must mean, as 17 cells in the full_api_ext table plus
the contract delta on both ext providers, so the implementation has a target
instead of a behaviour to bless.

The contract these cells encode:

  * `?T` is TWO-state — a value of T, or empty — never three-state. Forced by
    "one LIDL type maps to one type per language": every target has exactly one
    empty inhabitant (None / nullopt / invalid QVariant / undefined).
  * Omitted and explicit-null are the SAME state on DECODE and DIFFERENT on
    ENCODE. Empty is spelled by OMITTING the key in a NAMED slot (a record
    field) and by null in a POSITIONAL one (arg, return) — a positional slot has
    no key to omit and the arity must never change. The round trip is therefore
    CANONICALISING, not identity, and several expectations here are deliberately
    not the argument echoed back.
  * A present-but-wrong-typed value in an optional slot is STILL AN ERROR.
    Optional widens the domain by exactly one inhabitant; it does not disable
    type checking. `hostile/Optional/scalar/wrong-type` is the cell that stops
    `?T` = `any` from passing, which is what all three backends do today.
  * The two spellings the spec binds to one meaning (`? name: T` and `name: ?T`)
    must emit identical code. Both are in the contract on purpose: today they do
    not even agree at runtime — the field flag is checked against the base type,
    the type kind falls to `Ty::Any` and is not checked at all.

Both ext providers are authored AT THE TARGET, which means neither compiles
until the generator work lands, and the ext check is red at build time until
then. That is recorded in known-ext.json (OPT0) rather than hidden: a fixture
written against the current behaviour cannot be the thing the fix is measured
against. Every cell that cannot pass is registered (OPT1: optionality is
unimplemented; OPT2: an empty optional in a positional slot is unspeakable on a
wire where null already means METHOD_FAILED, and the py driver cannot send a
top-level null at all), so the suite is honestly red now and reports xpass —
which fails the run — the day the generators are right.

The registry entries record what was MEASURED about the generators by executing
them: --header-to-lidl silently derives `any` from std::optional, the cdylib
gate rejects `?T` outright, and the emitted record codec ignores the field flag
in both directions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore: re-pin logos-module-builder for the optional SDK stack

Carries logos-lidl#7, logos-protocol#37, logos-rust-sdk#31, logos-cpp-sdk#125
and the two module-builder re-pins (#177, #178) that make them reachable.

Both ext providers now BUILD, which is what OPT0 recorded as impossible:

  nix build .#test_fullapi_ext_cpp   -> exit 0  (was: undefined template
                                        Codec<std::optional<std::string>>)
  nix build .#test_fullapi_ext_rust  -> exit 0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* conformance: OPT1/OPT2 must declare which consumers they were measured on

run_matrix.py refuses to start otherwise:

  known.json xfail entry 'OPT1' has no `consumers`; an xfail must state which
  surfaces it was measured on

The entries were written while the ext providers could not build (OPT0), so
the driver had never reached them to reject them. Both are measured on the py
consumer, which is the only driver the ext table has.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* conformance: OPT1 is closed — measured, not assumed

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 09:23:59 -03:00
..