Skip to content

datagram@3 and comline@2 in the runtime (runtime#15, phase 2) - #80

Merged
Kinflou merged 1 commit into
mainfrom
feat/comline2-handshake
Nov 3, 2026
Merged

Kinflou merged 1 commit into
mainfrom
feat/comline2-handshake

Conversation

@Kinflou

@Kinflou Kinflou commented Nov 3, 2026

Copy link
Copy Markdown
Member

Phase 2 of comline@2 (runtime#15): the runtime side. Neither datagram@3 nor comline@2 becomes a default here; that is phase 3, in core. Generators are phase 4.

datagram@3

datagram@2 with a call addressed by wire numbers (calls = by_id):

  • Request: [0][protocol varint][function varint][request_id u64 LE][params…]
  • Reply: [1][protocol varint][request_id u64 LE][envelope…], the same as datagram@2

Both numbers are u16s written as shortest-form LEB128 (1–3 bytes). datagram@2 is unchanged. The new type is contract::Datagram3Framing.

comline@2

The new module is comline_runtime::comline2. Comline2 holds the sans-IO steps, and Comline2Handshake runs them as a Handshake/AsyncHandshake. It follows the decisions recorded in runtime#15:

  • Parameters: scope = protocol | package, newer_consumer = degrade | refuse, same_version_differs = refuse | warn and exchange = offer | symmetric.

    • exchange is a wire parameter. Each hello carries it, and the handshake encoding's parameter hash covers it.
    • The other three are each end's own policy.
  • Carried: the package id (16 bytes) and the package version. Per protocol: its number, its line and a 128-bit shape hash, plus the function numbers. Per encoding: its kind and a 128-bit parameter hash, which covers the name.

    • The namespace is never compared.
    • "since" stays local: it isn't needed on the wire, because degrade works on function numbers.
  • Lines decide. If the shape differs at the same package version, same_version_differs decides.

  • Messages: every message starts "CO", 2, type.

    • Hello (1), Answer (2) and Verdict (3).
    • Varints and numbers throughout, never names.
    • The layout is documented on comline2::Message.
  • Exchanges:

    • offer: Hello →, ← Answer (per offered protocol: accepted, with its missing functions, or refused with a reason), Verdict →.
    • symmetric: Hello ⇄, Verdict ⇄.
  • Refusals: each one carries a stable reason code and the refusing side. They surface as RuntimeError::Refused { reason, side }. The new type is contract::Reason:

    code reason scope
    1 malformed connection
    2 handshake_version (e.g. a comline@1 frame) connection
    3 exchange_mismatch connection
    4 package_mismatch connection
    5 encoding_mismatch connection
    6 line_mismatch protocol
    7 same_version_differs protocol
    8 newer_consumer protocol
    9 unknown_protocol protocol (never escalates under scope = package)
  • Degrade: after the handshake, Comline2Handshake::negotiated() gives a Negotiated.

    • check_call(protocol, function) returns RuntimeError::Unavailable for a function the peer lacks, or for a protocol that was refused. Generated code calls it before writing, so nothing reaches the wire.
    • Unavailable carries no fields. That keeps RuntimeError at 4 bytes, which the packed@1 decode path depends on.

Conformance, spec 0.4

  • New features: framing-datagram3 and handshake-comline2.
  • New ops: comline2_message (encode and decode) and comline2_negotiate (decode only). The negotiate op gives back the reply message and the result.
  • New profiles: datagram@3 (calls = by_id, errors = by_ordinal) and comline@2 (schema_agreement, encoding_agreement).
  • Vectors cover every parameter value and every reason code, across steps of both exchanges, verdicts and malformed messages.
  • comline-reference answers the new ops and lists datagram@3.
  • Tests: a core test runs both exchanges over the in-memory transport, covering accept, degrade and refusals on both ends.

Not here

  • The C ABI and wasm (runtime/ffi): neither speaks datagram@3 or comline@2 yet, and the TS runtime runs on that wasm build. So comline-typescript can't claim the new features until the ABI gains them.
  • Defaults (phase 3, core) and generators (phase 4).

…hake (runtime#15)

datagram@3 addresses a call by its protocol's and function's wire numbers,
both varints (calls = by_id); replies are datagram@2's, which is unchanged.

comline@2 compares what core's facts record instead of the whole IR: the
package id, encodings by parameter hash, and per protocol its number, line
and shape hash and its functions' numbers. Lines decide; a shape that
differs at the same version goes to same_version_differs. The parameters
scope, newer_consumer, same_version_differs and exchange (offer or
symmetric, a wire parameter) are all implemented, sans-IO on Comline2 and
over blocking and async transports on Comline2Handshake. Refusals carry a
stable reason code and the refusing side (RuntimeError::Refused); calls
the peer can't take fail locally through Negotiated::check_call
(RuntimeError::Unavailable).

Conformance spec 0.4: features framing-datagram3 and handshake-comline2,
ops comline2_message and comline2_negotiate, profiles datagram@3 and
comline@2, and vectors for every parameter value and every reason code.
Neither is a default yet.
@Kinflou
Kinflou merged commit 8189aa4 into main Nov 3, 2026
24 checks passed
@Kinflou
Kinflou deleted the feat/comline2-handshake branch November 3, 2026 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

1 participant