Skip to content

Float width is not pinned, so auto: keys can diverge across runtimes #905

Description

@kartikeya-27

contracts/wire-vectors.json leaves float width free, by design:

An encoder may legitimately pick a narrower width, so only the decoded value
is pinned and the bytes are left free. float is the example: a writer may
emit a half or single-precision float for 1.5.

Definite-length containers and shortest-form integers are pinned for a stated
reason — the auto: idempotency key is a sha256 over the serialized payload, so
a divergent writer interoperates fine and silently stops idempotent enqueues
deduping across runtimes. A float width is the same class of divergence and is
not pinned.
Two conforming encoders can derive different auto: keys for the
same float argument, and each enqueues its own active job.

The gap is narrow but real:

  • Rust never narrows — crates/flexiq-core/src/wire/cbor.rs writes HEAD_F64
    unconditionally, so flexiq-core and everything encoding through it agree.
  • A shell using its language's own CBOR library is where it bites. Streaming
    writers commonly emit the shortest float that round-trips, which is exactly
    how the indefinite-length divergence arrived (feat: pin the cross-SDK call envelope as shared wire vectors #638).
  • Reachable whenever a task takes a float argument and the caller relies on
    @task(idempotent=True) / auto: rather than setting unique_key.

Options

  1. Pin the shortest float width that preserves the value, add a vector for
    it, and assert it in all four suites. Matches how shortest-form integers are
    already handled, and closes the class.
  2. Pin f64 always, which is what Rust already does and is the cheaper
    assertion, at the cost of bytes.
  3. Leave it and document the consequence. PR docs: write down the contract a gRPC-only client owes #904 does this much: the remote
    contract now says an auto: key over a float payload may not agree across
    runtimes and that such a client should set unique_key itself.

Option 3 is in already and is not a fix — it tells a client to avoid the
feature. Either 1 or 2 is a wire-format decision touching
contracts/wire-vectors.json, crates/flexiq-core/BINDING_CONTRACT.md and the
Python, Node and Java conformance suites, so it wants its own change rather than
riding a documentation PR.

Raised from a review comment on #904, which proposed tightening the rule in the
contract document alone. That was declined there: the document restates
wire-vectors.json and BINDING_CONTRACT.md, and a normative file that
unilaterally tightens what its own sources leave loose creates two answers to
one question.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Opportunisticarea/wireNetwork contract: proto, gRPC, JSON facadebugSomething isn't workingrust

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions