[v0.1/P1] Remove minor-reset fallbacks and duplicate status identity #333

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

Problem

#313 reset the first public protocol to minor 0, and #330 owns physical schema cleanup such as pre-baseline reserved numbers and ConnWelcome.build_id. Several semantic fallbacks and duplicate authorities remain outside #330's scope, however. Freezing them would preserve compatibility with development minors that no public peer can run.

Current cases:

  • SessionInfo.sequence remains optional and the Rust domain retains an absent/zero fallback for a daemon predating the sequence field. Every public 1.0 daemon can be required to provide a positive sequence.
  • CLI ErrorKind::ProtocolMismatch remains as a frozen machine token for consumers that do not yet exist, although negotiated refusal/error vocabulary now expresses the actual failures.
  • OpsStatusReply repeats build and protocol identity already established by the mandatory connection handshake, so a client can observe two authorities on one connection.
  • unknown ConnectionMode refusal recovers the raw integer by bypassing the normal validated codec. #312 correctly requires typed deterministic handling, but it does not require two wire interpretations.

This is distinct from #115: that issue installs and advances the release baseline. This issue removes semantic development-minor residue before #115 records it.

Proposed resolution

  1. Make positive session sequence a semantic requirement of public protocol 1.0. Decode absent or zero as malformed instead of falling back to pre-sequence ordering.
  2. Remove machine-output error tokens that have no reachable public producer. Add future tokens when a public operation can produce them, under the normal additive machine-output policy.
  3. Keep handshake metadata authoritative. Status replies should carry live daemon state; clients should compose build/protocol identity from the established connection rather than receiving a second copy.
  4. Preserve #312's deterministic unknown-mode refusal only if it can be represented through the normal validated decode path. Otherwise record why this single arm must bypass that path before freezing it.

Do not retain behavior merely because an unreleased minor once needed it. Any retained fallback needs a current semantic use stated in the owning explanation document.

Acceptance criteria

  • Session sequence has one public meaning and no old-development-daemon fallback.
  • No CLI machine error token exists solely for an unreachable pre-release failure path.
  • Handshake and status have one authority for build/protocol identity.
  • Connection-mode decode follows one codec path, or the retained exception has a documented reason that survives the public baseline.
  • Rust domain types, conversion code, CLI output, IPC docs, CHANGELOG.md, and skills/felis are updated as required.
  • This lands after/coordinated with #330 and before #115 records the first release baseline.

Evidence

  • crates/felis-protocol/proto/felis.proto
  • crates/felis-protocol/src/convert/conn.rs
  • crates/felis-protocol/src/messages/ops.rs
  • crates/felis-client-core/src/connector.rs
  • crates/felis-cli/src/cli_output.rs
  • docs/reference/ipc.md
  • docs/explanation/architecture/ipc.md

Related: #17, #115, #144, #145, #312, #313, #330.

## Problem #313 reset the first public protocol to minor 0, and #330 owns physical schema cleanup such as pre-baseline `reserved` numbers and `ConnWelcome.build_id`. Several semantic fallbacks and duplicate authorities remain outside #330's scope, however. Freezing them would preserve compatibility with development minors that no public peer can run. Current cases: - `SessionInfo.sequence` remains optional and the Rust domain retains an absent/zero fallback for a daemon predating the sequence field. Every public 1.0 daemon can be required to provide a positive sequence. - CLI `ErrorKind::ProtocolMismatch` remains as a frozen machine token for consumers that do not yet exist, although negotiated refusal/error vocabulary now expresses the actual failures. - `OpsStatusReply` repeats build and protocol identity already established by the mandatory connection handshake, so a client can observe two authorities on one connection. - unknown `ConnectionMode` refusal recovers the raw integer by bypassing the normal validated codec. #312 correctly requires typed deterministic handling, but it does not require two wire interpretations. This is distinct from #115: that issue installs and advances the release baseline. This issue removes semantic development-minor residue before #115 records it. ## Proposed resolution 1. Make positive session sequence a semantic requirement of public protocol 1.0. Decode absent or zero as malformed instead of falling back to pre-sequence ordering. 2. Remove machine-output error tokens that have no reachable public producer. Add future tokens when a public operation can produce them, under the normal additive machine-output policy. 3. Keep handshake metadata authoritative. Status replies should carry live daemon state; clients should compose build/protocol identity from the established connection rather than receiving a second copy. 4. Preserve #312's deterministic unknown-mode refusal only if it can be represented through the normal validated decode path. Otherwise record why this single arm must bypass that path before freezing it. Do not retain behavior merely because an unreleased minor once needed it. Any retained fallback needs a current semantic use stated in the owning explanation document. ## Acceptance criteria - [ ] Session sequence has one public meaning and no old-development-daemon fallback. - [ ] No CLI machine error token exists solely for an unreachable pre-release failure path. - [ ] Handshake and status have one authority for build/protocol identity. - [ ] Connection-mode decode follows one codec path, or the retained exception has a documented reason that survives the public baseline. - [ ] Rust domain types, conversion code, CLI output, IPC docs, `CHANGELOG.md`, and `skills/felis` are updated as required. - [ ] This lands after/coordinated with #330 and before #115 records the first release baseline. ## Evidence - `crates/felis-protocol/proto/felis.proto` - `crates/felis-protocol/src/convert/conn.rs` - `crates/felis-protocol/src/messages/ops.rs` - `crates/felis-client-core/src/connector.rs` - `crates/felis-cli/src/cli_output.rs` - `docs/reference/ipc.md` - `docs/explanation/architecture/ipc.md` Related: #17, #115, #144, #145, #312, #313, #330.
natsukium changed title from [v0.1/P1] Remove pre-release compatibility residue before the first wire baseline to [v0.1/P1] Remove minor-reset fallbacks and duplicate status identity 2026-09-21 23:20:35 +09:00
Author
Owner

Decision: do items 1, 2, 3; item 4 is retained with a recorded reason (reviewed)

  1. SessionInfo.sequence becomes required (uint64, 0 = malformed). The daemon always sends it; the roster.rs zero fallback and the convert None path go.
  2. ErrorKind::ProtocolMismatch is removed, as a coordinated cascade, not a one-line deletion. No producer exists in crates/, and felis.el does not branch on it. The token is mirrored in cli.md, felis-cli-v1.schema.json, and the golden error_kinds.txt, and the source comment calls it frozen in v1; that freeze is against a consumer that does not exist yet, so removing it now is the same pre-release break the proto batch makes, not a v bump. Source, regenerated schema, golden, cli.md, CHANGELOG.md, skills/felis if it lists the vocabulary. Re-add under the additive policy when a public operation can produce it.
  3. OpsStatusReply drops version, protocol_major, protocol_minor; the CLI JSON shape is unchanged. Build identity comes from Welcome.identity, the major from the accepted preface, and the daemon's own minor from DaemonPreface::Accept, which the connector currently collapses into effective_minor and discards (an older client on a newer daemon would misreport it, so the effective minor is not a substitute). The connector keeps the accepted major and the daemon's minor on Connection, and felis daemon status fills the same keys from there. This PR also fixes the protocol.minor: 10 example in cli.md.
  4. The unknown-mode second decode (serve.rs unreadable_mode) is retained and recorded as a diagnostic for non-conforming peers. A conforming client refuses a mode above the effective minor before Hello (check_mode_minor), so the path never sees ordinary skew; it is the daemon's documented half of the ledger rule (ipc.md: Refused { UnknownMode } rather than a silent close, settled in #312) and is already pinned by serve/tests.rs (a_hello_naming_an_unknown_mode_is_refused_by_name, an_unreadable_hello_that_is_not_a_future_mode_closes_unanswered). Only the explanation record changes.

Lands last in the proto batch (after #327), own base: line.

## Decision: do items 1, 2, 3; item 4 is retained with a recorded reason (reviewed) 1. **`SessionInfo.sequence` becomes required** (`uint64`, 0 = malformed). The daemon always sends it; the `roster.rs` zero fallback and the `convert` `None` path go. 2. **`ErrorKind::ProtocolMismatch` is removed, as a coordinated cascade, not a one-line deletion.** No producer exists in `crates/`, and `felis.el` does not branch on it. The token is mirrored in `cli.md`, `felis-cli-v1.schema.json`, and the golden `error_kinds.txt`, and the source comment calls it frozen in `v1`; that freeze is against a consumer that does not exist yet, so removing it now is the same pre-release break the proto batch makes, not a `v` bump. Source, regenerated schema, golden, `cli.md`, `CHANGELOG.md`, `skills/felis` if it lists the vocabulary. Re-add under the additive policy when a public operation can produce it. 3. **`OpsStatusReply` drops `version`, `protocol_major`, `protocol_minor`; the CLI JSON shape is unchanged.** Build identity comes from `Welcome.identity`, the major from the accepted preface, and the daemon's *own* minor from `DaemonPreface::Accept`, which the connector currently collapses into `effective_minor` and discards (an older client on a newer daemon would misreport it, so the effective minor is not a substitute). The connector keeps the accepted major and the daemon's minor on `Connection`, and `felis daemon status` fills the same keys from there. This PR also fixes the `protocol.minor: 10` example in `cli.md`. 4. **The unknown-mode second decode (`serve.rs` `unreadable_mode`) is retained and recorded as a diagnostic for non-conforming peers.** A conforming client refuses a mode above the effective minor before `Hello` (`check_mode_minor`), so the path never sees ordinary skew; it is the daemon's documented half of the ledger rule (`ipc.md`: `Refused { UnknownMode }` rather than a silent close, settled in #312) and is already pinned by `serve/tests.rs` (`a_hello_naming_an_unknown_mode_is_refused_by_name`, `an_unreadable_hello_that_is_not_a_future_mode_closes_unanswered`). Only the explanation record changes. Lands last in the proto batch (after #327), own `base:` line.
Commenting is not possible because the repository is archived.
No description provided.