protocol: post-freeze observability and descriptor completeness (effective minor, field-level since, bit tables) #351

Open
opened 2026-09-22 20:37:47 +09:00 by natsukium · 1 comment
Owner

Context

Split from #347. The pre-release review of the frozen surfaces found these three gaps; each is an additive change (a new field, a new custom option, or a descriptor-carried table), so under the versioning contract it lands as a post-release minor and does not need to precede the v0.1.0 freeze. #347 keeps the items that are cheaper before the freeze than after it (the _UNSPECIFIED mode test, the --timeout 0 definition, and the reference wording).

1. The negotiated effective minor is unobservable

ConnWelcome echoes nothing, and OpsStatusReply deliberately carries no version (#343). The effective minor, min(client, daemon), is computed on both ends and published nowhere: not in Welcome, not in felis daemon status, not in any machine output. Once the ledger grows, "my old CLI can't use arm X" is debuggable only through the send gate's refusal string, and minor adoption has no observation surface.

Proposal: one informational field on ConnWelcome (effective_minor, or an echo of client_minor). Display and diagnostics only; the preface stays the sole version authority.

2. Field-level minor metadata covers only six messages

crates/felis-protocol/src/minor.rs declares field requirements in hand-written tables (CONN_FIELDS, SESSION_FIELDS, OPS_FIELDS, PUSH_FIELDS, SESSION_INFO_FIELDS, SPAWN_ARGS_FIELDS), and gated_by_arm! covers Input / Image / Notify / Region / Search on the assumption that no post-baseline field or enum value rides inside one. A field added later to SearchQuery, ImageNew, RegionRequest, NotifyEvent, InputResize, and so on has no place to declare its minor: requires() does not rise, the_ledger_covers_every_executable_addition still passes, and protobuf's unknown-field tolerance makes an older peer ignore the field silently instead of the send gate refusing it.

Proposal: a (felis.v1.since) field option in the schema (or an extension of the ArmRouting pattern), with the ledger test enumerating every field from the descriptor and requiring a minor and a ledger row for each post-baseline one. The GatedField tables become derived.

3. Bitmap layouts are not in the descriptor

felis.proto says bitflags carry "raw bits; see messages.rs" for InputMouseEvent.mods, InputKeyEvent.mods, and the type note near the top. The KeyMods layout is in docs/reference/ipc.md; the mouse InputMods triple is being added to the Mouse bullet in a doc PR. What remains is the cross-language concern: a non-Rust peer reading only the descriptor cannot encode or decode these, while ipc.md "Application messages" promises the descriptor needs no other manifest. GridClipboardSet.selection states its bits inline, so the pattern exists.

Proposal: carry the bit tables as a field option read from the descriptor (the (felis.v1.arm) shape), or at minimum make the proto comments normative and name the standard each set follows. GridKittyKbdFlags.flags deserves the same treatment.

Low-priority shape notes

Recorded so they are not re-found; none is worth a change on its own.

  • Row indices have three widths: SearchMatch.line_index / ColSpan.line_index are sint64, RegionRow.row is sint32, RowEntry.row is uint32.
  • ResolvedId.ambiguous is a bare uint32 arm while OpsInfoReply.ambiguous is an OpsInfoAmbiguous { matches } message; SpawnRefused and SessionAttachFailed place detail differently.

Acceptance criteria

  • Each of the three items lands as a ledger row, or is recorded as rejected in the owning explanation doc.
  • the_ledger_covers_every_executable_addition enumerates fields from the descriptor.
  • docs/reference/ipc.md "Application messages" is true: the descriptor alone suffices for a non-Rust peer.
## Context Split from #347. The pre-release review of the frozen surfaces found these three gaps; each is an additive change (a new field, a new custom option, or a descriptor-carried table), so under the versioning contract it lands as a post-release minor and does not need to precede the v0.1.0 freeze. #347 keeps the items that are cheaper before the freeze than after it (the `_UNSPECIFIED` mode test, the `--timeout 0` definition, and the reference wording). ## 1. The negotiated effective minor is unobservable `ConnWelcome` echoes nothing, and `OpsStatusReply` deliberately carries no version (#343). The effective minor, `min(client, daemon)`, is computed on both ends and published nowhere: not in `Welcome`, not in `felis daemon status`, not in any machine output. Once the ledger grows, "my old CLI can't use arm X" is debuggable only through the send gate's refusal string, and minor adoption has no observation surface. Proposal: one informational field on `ConnWelcome` (`effective_minor`, or an echo of `client_minor`). Display and diagnostics only; the preface stays the sole version authority. ## 2. Field-level minor metadata covers only six messages `crates/felis-protocol/src/minor.rs` declares field requirements in hand-written tables (`CONN_FIELDS`, `SESSION_FIELDS`, `OPS_FIELDS`, `PUSH_FIELDS`, `SESSION_INFO_FIELDS`, `SPAWN_ARGS_FIELDS`), and `gated_by_arm!` covers `Input` / `Image` / `Notify` / `Region` / `Search` on the assumption that no post-baseline field or enum value rides inside one. A field added later to `SearchQuery`, `ImageNew`, `RegionRequest`, `NotifyEvent`, `InputResize`, and so on has no place to declare its minor: `requires()` does not rise, `the_ledger_covers_every_executable_addition` still passes, and protobuf's unknown-field tolerance makes an older peer ignore the field silently instead of the send gate refusing it. Proposal: a `(felis.v1.since)` field option in the schema (or an extension of the `ArmRouting` pattern), with the ledger test enumerating every field from the descriptor and requiring a minor and a ledger row for each post-baseline one. The `GatedField` tables become derived. ## 3. Bitmap layouts are not in the descriptor `felis.proto` says bitflags carry "raw bits; see messages.rs" for `InputMouseEvent.mods`, `InputKeyEvent.mods`, and the type note near the top. The `KeyMods` layout is in `docs/reference/ipc.md`; the mouse `InputMods` triple is being added to the `Mouse` bullet in a doc PR. What remains is the cross-language concern: a non-Rust peer reading only the descriptor cannot encode or decode these, while ipc.md "Application messages" promises the descriptor needs no other manifest. `GridClipboardSet.selection` states its bits inline, so the pattern exists. Proposal: carry the bit tables as a field option read from the descriptor (the `(felis.v1.arm)` shape), or at minimum make the proto comments normative and name the standard each set follows. `GridKittyKbdFlags.flags` deserves the same treatment. ## Low-priority shape notes Recorded so they are not re-found; none is worth a change on its own. - Row indices have three widths: `SearchMatch.line_index` / `ColSpan.line_index` are `sint64`, `RegionRow.row` is `sint32`, `RowEntry.row` is `uint32`. - `ResolvedId.ambiguous` is a bare `uint32` arm while `OpsInfoReply.ambiguous` is an `OpsInfoAmbiguous { matches }` message; `SpawnRefused` and `SessionAttachFailed` place `detail` differently. ## Acceptance criteria - [ ] Each of the three items lands as a ledger row, or is recorded as rejected in the owning explanation doc. - [ ] `the_ledger_covers_every_executable_addition` enumerates fields from the descriptor. - [ ] `docs/reference/ipc.md` "Application messages" is true: the descriptor alone suffices for a non-Rust peer.
Author
Owner

Triage 2026-09-25: item 1 is partly addressed. felis doctor now prints the negotiated wire version from conn.effective_minor (crates/felis-cli/src/cli_doctor.rs), but ConnWelcome still carries no such field and felis daemon status still reports only the daemon's ceiling, so the wire-level observability gap stands. Items 2 and 3 are unchanged: the field-level since tables in crates/felis-protocol/src/minor.rs are all empty (mechanism dormant), and bitmap layouts are still comment-only, not in the descriptor.

Triage 2026-09-25: item 1 is partly addressed. `felis doctor` now prints the negotiated wire version from `conn.effective_minor` (`crates/felis-cli/src/cli_doctor.rs`), but `ConnWelcome` still carries no such field and `felis daemon status` still reports only the daemon's ceiling, so the wire-level observability gap stands. Items 2 and 3 are unchanged: the field-level since tables in `crates/felis-protocol/src/minor.rs` are all empty (mechanism dormant), and bitmap layouts are still comment-only, not in the descriptor.
Commenting is not possible because the repository is archived.
No description provided.