[v0.1/P1] Make operation defaults and attach targets explicit oneof arms #334

Closed
opened 2026-09-21 23:17:30 +09:00 by natsukium · 1 comment
Owner

Problem

Several operation choices use an unset protobuf oneof or independent sibling fields as a valid command. This makes malformed input indistinguishable from an intentional choice and gives unknown future arms unsafe downgrade behavior.

Current cases:

  • An unset RetargetTarget.carrier means DefaultLocal.
  • An unset OpsStop.mode means IfEmpty.
  • SessionAttach.id and id_prefix are independent fields; conversion manually rejects both-set and neither-set.

For carrier and stop mode, an older decoder drops an unknown future oneof arm and sees None. The current conversion then executes a real default operation instead of rejecting a command it cannot understand. For attach, the schema admits two impossible combinations that the domain layer must repair.

Proposed resolution

Represent every operation choice explicitly in felis.proto:

  • add an empty-message default_local arm to the retarget carrier union;
  • add an empty-message if_empty arm to the stop-mode union;
  • place attach id and id_prefix in a oneof target, retaining their existing field numbers.

Unset unions must decode as MissingOneof. Unknown future arms then also fail closed on an older decoder instead of being reinterpreted as an existing operation.

This applies to operation/default semantics, not ordinary optional state. A field whose absence means “state is unavailable” need not become an empty-message arm.

Acceptance criteria

  • Every valid retarget carrier and stop mode is represented by an explicit oneof arm.
  • Unset retarget/stop unions are rejected as malformed.
  • Attach target is a schema-level oneof using the existing id and id_prefix field numbers.
  • Both-set/neither-set attach states cannot be produced by generated protobuf APIs; unset still fails validation.
  • Tests cover each explicit arm, unset unions, and an unknown future arm being rejected rather than mapped to a default.
  • The wire tables and rationale in IPC reference/explanation docs are updated.
  • The breaking pre-release schema change lands before the first immutable baseline.

Evidence

  • crates/felis-protocol/proto/felis.proto (RetargetTarget, OpsStop, SessionAttach)
  • crates/felis-protocol/src/convert/conn.rs
  • crates/felis-protocol/src/convert/ops.rs
  • crates/felis-protocol/src/convert/session.rs
  • docs/reference/ipc.md

Related: #115, #313.

## Problem Several operation choices use an unset protobuf `oneof` or independent sibling fields as a valid command. This makes malformed input indistinguishable from an intentional choice and gives unknown future arms unsafe downgrade behavior. Current cases: - An unset `RetargetTarget.carrier` means `DefaultLocal`. - An unset `OpsStop.mode` means `IfEmpty`. - `SessionAttach.id` and `id_prefix` are independent fields; conversion manually rejects both-set and neither-set. For carrier and stop mode, an older decoder drops an unknown future oneof arm and sees `None`. The current conversion then executes a real default operation instead of rejecting a command it cannot understand. For attach, the schema admits two impossible combinations that the domain layer must repair. ## Proposed resolution Represent every operation choice explicitly in `felis.proto`: - add an empty-message `default_local` arm to the retarget carrier union; - add an empty-message `if_empty` arm to the stop-mode union; - place attach `id` and `id_prefix` in a `oneof target`, retaining their existing field numbers. Unset unions must decode as `MissingOneof`. Unknown future arms then also fail closed on an older decoder instead of being reinterpreted as an existing operation. This applies to operation/default semantics, not ordinary optional state. A field whose absence means “state is unavailable” need not become an empty-message arm. ## Acceptance criteria - [ ] Every valid retarget carrier and stop mode is represented by an explicit oneof arm. - [ ] Unset retarget/stop unions are rejected as malformed. - [ ] Attach target is a schema-level oneof using the existing `id` and `id_prefix` field numbers. - [ ] Both-set/neither-set attach states cannot be produced by generated protobuf APIs; unset still fails validation. - [ ] Tests cover each explicit arm, unset unions, and an unknown future arm being rejected rather than mapped to a default. - [ ] The wire tables and rationale in IPC reference/explanation docs are updated. - [ ] The breaking pre-release schema change lands before the first immutable baseline. ## Evidence - `crates/felis-protocol/proto/felis.proto` (`RetargetTarget`, `OpsStop`, `SessionAttach`) - `crates/felis-protocol/src/convert/conn.rs` - `crates/felis-protocol/src/convert/ops.rs` - `crates/felis-protocol/src/convert/session.rs` - `docs/reference/ipc.md` Related: #115, #313.
Author
Owner

Duplicate of #327, which already owns the same SessionAttach, RetargetTarget, and OpsStop oneof changes and also covers reset spellings.

Duplicate of #327, which already owns the same SessionAttach, RetargetTarget, and OpsStop oneof changes and also covers reset spellings.
Commenting is not possible because the repository is archived.
No description provided.