protocol: name every arm instead of letting absence mean one #342

Merged
natsukium merged 1 commit from issue-327 into main 2026-09-22 10:26:27 +09:00
Owner

Closes #327

In proto3 a field a peer omitted and a field a peer never knew are the
same bytes, so any union whose unset state carries meaning reads an arm
added after the freeze as whatever the schema nominated as its default.
An old decoder then performs a real operation in place of the one it
could not understand. The first public baseline is the last point at
which naming every arm is free, so all four remaining cases get named
arms: RetargetTarget.carrier gains default_local, SessionAttach's id and
id_prefix become a target oneof at their current numbers, and
GridThemeColor takes the set/reset shape GridPaletteColor already had.
OpsStop.mode already had if_empty; only its decoder defaulted.

Rejected: keeping unset as the default arm, which reads well because the
common case costs no bytes and a producer writing nothing gets the safe
posture. It makes a frame the decoder could not understand identical to
one that meant the default, and those two want opposite answers; it also
puts the default in the reader, so peers built a minor apart disagree
about what was asked while both decode cleanly. SwitchScope already
refused an unset union, and one policy across the families is what keeps
a reader from having to remember which family is which.

Refs #327.

Review

pi luna and sol reviews on the pre-rebase commit both passed (non-blocking findings folded: BREAKING.md now describes the one-sided skew, and the stale ThemeColor.rgb doc comment is gone). Rebased onto #340/#341; just proto-compat, fmt-check, prose-check, clippy and the Windows cross clippy pass locally. The workspace test run shows only the pre-existing socket-path-length failures on this machine (felis-cli cli_bridge lost-daemon / relative-cwd, felis-daemon racing teardown / parked peer).

The Criterion regression gate is a known-flaky bench job and fails on every PR.

Closes #327 In proto3 a field a peer omitted and a field a peer never knew are the same bytes, so any union whose unset state carries meaning reads an arm added after the freeze as whatever the schema nominated as its default. An old decoder then performs a real operation in place of the one it could not understand. The first public baseline is the last point at which naming every arm is free, so all four remaining cases get named arms: RetargetTarget.carrier gains default_local, SessionAttach's id and id_prefix become a target oneof at their current numbers, and GridThemeColor takes the set/reset shape GridPaletteColor already had. OpsStop.mode already had if_empty; only its decoder defaulted. Rejected: keeping unset as the default arm, which reads well because the common case costs no bytes and a producer writing nothing gets the safe posture. It makes a frame the decoder could not understand identical to one that meant the default, and those two want opposite answers; it also puts the default in the reader, so peers built a minor apart disagree about what was asked while both decode cleanly. SwitchScope already refused an unset union, and one policy across the families is what keeps a reader from having to remember which family is which. Refs #327. ## Review pi luna and sol reviews on the pre-rebase commit both passed (non-blocking findings folded: BREAKING.md now describes the one-sided skew, and the stale `ThemeColor.rgb` doc comment is gone). Rebased onto #340/#341; `just proto-compat`, fmt-check, prose-check, clippy and the Windows cross clippy pass locally. The workspace test run shows only the pre-existing socket-path-length failures on this machine (felis-cli cli_bridge lost-daemon / relative-cwd, felis-daemon racing teardown / parked peer). The Criterion regression gate is a known-flaky bench job and fails on every PR.
protocol: name every arm instead of letting absence mean one
Some checks failed
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) Failing after 2m23s
darwin / detect relevant changes (pull_request) Successful in 4s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 52s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m22s
pr / nix flake check (pull_request) Failing after 34s
pr / cargo build / clippy / test / deny (pull_request) Failing after 12s
pr / wire schema is compatible with the base (pull_request) Successful in 11s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 1m6s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 4s
windows / windows docs-only no-op (pull_request) Has been skipped
windows / frontend smoke (Windows) (pull_request) Successful in 1m43s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
windows / cargo clippy (Windows cross) (pull_request) Successful in 14s
windows / cargo nextest (Windows) (pull_request) Successful in 7m15s
d0fc665ec8
In proto3 a field a peer omitted and a field a peer never knew are the
same bytes, so any union whose unset state carries meaning reads an arm
added after the freeze as whatever the schema nominated as its default.
An old decoder then performs a real operation in place of the one it
could not understand. The first public baseline is the last point at
which naming every arm is free, so all four remaining cases get named
arms: RetargetTarget.carrier gains default_local, SessionAttach's id and
id_prefix become a target oneof at their current numbers, and
GridThemeColor takes the set/reset shape GridPaletteColor already had.
OpsStop.mode already had if_empty; only its decoder defaulted.

Rejected: keeping unset as the default arm, which reads well because the
common case costs no bytes and a producer writing nothing gets the safe
posture. It makes a frame the decoder could not understand identical to
one that meant the default, and those two want opposite answers; it also
puts the default in the reader, so peers built a minor apart disagree
about what was asked while both decode cleanly. SwitchScope already
refused an unset union, and one policy across the families is what keeps
a reader from having to remember which family is which.

Refs #327.

Assisted-by: Claude Code
natsukium force-pushed issue-327 from d0fc665ec8
Some checks failed
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) Failing after 2m23s
darwin / detect relevant changes (pull_request) Successful in 4s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 52s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m22s
pr / nix flake check (pull_request) Failing after 34s
pr / cargo build / clippy / test / deny (pull_request) Failing after 12s
pr / wire schema is compatible with the base (pull_request) Successful in 11s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 1m6s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 4s
windows / windows docs-only no-op (pull_request) Has been skipped
windows / frontend smoke (Windows) (pull_request) Successful in 1m43s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
windows / cargo clippy (Windows cross) (pull_request) Successful in 14s
windows / cargo nextest (Windows) (pull_request) Successful in 7m15s
to 9093211109
Some checks failed
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) Failing after 5m18s
darwin / detect relevant changes (pull_request) Successful in 3s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 51s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m11s
pr / nix flake check (pull_request) Successful in 3m32s
pr / cargo build / clippy / test / deny (pull_request) Successful in 5m24s
pr / wire schema is compatible with the base (pull_request) Successful in 1m0s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 3m11s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 4s
windows / windows docs-only no-op (pull_request) Has been skipped
windows / cargo clippy (Windows cross) (pull_request) Successful in 41s
windows / cargo nextest (Windows) (pull_request) Successful in 4m40s
windows / frontend smoke (Windows) (pull_request) Successful in 1m39s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
2026-09-22 09:47:10 +09:00
Compare
natsukium deleted branch issue-327 2026-09-22 10:26:28 +09:00
Commenting is not possible because the repository is archived.
No description provided.