transport: take the writer's effective minor at construction #231

Merged
natsukium merged 1 commit from issue-176 into main 2026-09-08 21:49:17 +09:00
Owner

FrameWriter::new defaulted its effective_minor to this build's own PROTOCOL_MINOR, so the plain public constructor
plus send() authorized every addition this build knows even though no peer had agreed to any of them — the send gate
was only as strong as the value a forgotten .at_minor call left behind. The minor is now a new parameter, so no send
path can inherit the ceiling by omission. A carrier whose preface has not run takes FrameWriter::baseline (base schema
only); a harness that is both peers takes FrameWriter::at_build_minor, gated behind the test-util feature so no
production dependency compiles it. Connection::from_halves reads the minor off the writer it is handed instead of
taking a second copy that could disagree with it.

Pinned by an_unnegotiated_writer_authorizes_only_the_base_schema (felis-transport): an unnegotiated writer refuses a
minor-1 addition with MinorTooOld and still sends a base-schema Hello on the same writer. The mandatory-parameter
half is pinned by the type system — every call site now states its minor. docs/reference/ipc.md "Versioning" records
the resulting fact. No wire, CLI, config or default changes, so no CHANGELOG entry.

Fixes #176

`FrameWriter::new` defaulted its `effective_minor` to this build's own `PROTOCOL_MINOR`, so the plain public constructor plus `send()` authorized every addition this build knows even though no peer had agreed to any of them — the send gate was only as strong as the value a forgotten `.at_minor` call left behind. The minor is now a `new` parameter, so no send path can inherit the ceiling by omission. A carrier whose preface has not run takes `FrameWriter::baseline` (base schema only); a harness that is both peers takes `FrameWriter::at_build_minor`, gated behind the `test-util` feature so no production dependency compiles it. `Connection::from_halves` reads the minor off the writer it is handed instead of taking a second copy that could disagree with it. Pinned by `an_unnegotiated_writer_authorizes_only_the_base_schema` (felis-transport): an unnegotiated writer refuses a minor-1 addition with `MinorTooOld` and still sends a base-schema `Hello` on the same writer. The mandatory-parameter half is pinned by the type system — every call site now states its minor. `docs/reference/ipc.md` "Versioning" records the resulting fact. No wire, CLI, config or default changes, so no CHANGELOG entry. Fixes #176
transport: take the writer's effective minor at construction
All checks were successful
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Successful in 2m10s
darwin / detect relevant changes (pull_request) Successful in 3s
darwin / darwin docs-only no-op (pull_request) Has been skipped
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m14s
darwin / build felis (aarch64-darwin) (pull_request) Successful in 2m6s
pr / nix flake check (pull_request) Successful in 1m11s
pr / wire schema is compatible with the base (pull_request) Successful in 8s
pr / cargo build / clippy / test / deny (pull_request) Successful in 1m55s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 3m7s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 3s
windows / windows docs-only no-op (pull_request) Has been skipped
windows / cargo nextest (Windows) (pull_request) Successful in 4m42s
windows / frontend smoke (Windows) (pull_request) Successful in 1m56s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
windows / cargo clippy (Windows cross) (pull_request) Successful in 14s
06bf6d1307
`FrameWriter::new` defaulted `effective_minor` to this build's own
`PROTOCOL_MINOR`, so a writer that had negotiated nothing authorized
every addition this build knows. The send gate is only as strong as its
default: a call site that forgot to narrow the minor emitted additions
no peer had agreed to, and nothing in the ordinary API said so.

The minor is now a `new` parameter, so no send path can inherit the
ceiling by omission. A carrier whose preface has not run takes
`baseline`, which authorizes the base schema alone; a harness that is
both peers takes `at_build_minor`, gated behind `test-util` so no
production dependency compiles it. `Connection::from_halves` reads the
minor off the writer it is handed rather than taking a second copy that
could disagree with it.

Assisted-by: Claude Code
natsukium deleted branch issue-176 2026-09-08 21:49:17 +09:00
Sign in to join this conversation.
No description provided.