[v0.1/P1] Do not silently drop out-of-range Grid RowDelta rows #304

Closed
opened 2026-09-21 12:08:09 +09:00 by natsukium · 1 comment
Owner

Problem

felis-client-core silently drops a GridMsg::RowDelta when row >= MAX_SHADOW_ROWS in ShadowScreen::write_row.

This avoids an allocation, but a valid protobuf frame with an out-of-range row can leave the client shadow diverged from the daemon without a protocol error, resync request, or user-visible indication. The same path currently combines resize-race tolerance with corruption handling.

Proposed resolution

Separate expected resize races from invalid wire data. For an out-of-range row, either:

  • reject the frame/connection as corruption, or
  • request a complete shadow resynchronization and make the recovery visible to the driver.

Keep the legitimate resize overshoot behavior for rows within the documented race bound, but do not silently discard rows beyond the protocol's admitted limits.

Acceptance criteria

  • The behavior for an out-of-range RowDelta.row is specified in docs/reference/ipc.md and the row-codec/renderer documentation as appropriate.
  • A test proves that an invalid row cannot silently leave the shadow in an unreported divergent state.
  • Resize-race tests continue to prove that valid rehydrate/damage bursts are accepted.
  • Receiver-side allocation limits remain enforced.

Sources

  • crates/felis-client-core/src/shadow.rs (write_row)
  • crates/felis-protocol/src/messages.rs (grid limits)
  • docs/reference/ipc.md (semantic limits and corruption behavior)
## Problem `felis-client-core` silently drops a `GridMsg::RowDelta` when `row >= MAX_SHADOW_ROWS` in `ShadowScreen::write_row`. This avoids an allocation, but a valid protobuf frame with an out-of-range row can leave the client shadow diverged from the daemon without a protocol error, resync request, or user-visible indication. The same path currently combines resize-race tolerance with corruption handling. ## Proposed resolution Separate expected resize races from invalid wire data. For an out-of-range row, either: - reject the frame/connection as corruption, or - request a complete shadow resynchronization and make the recovery visible to the driver. Keep the legitimate resize overshoot behavior for rows within the documented race bound, but do not silently discard rows beyond the protocol's admitted limits. ## Acceptance criteria - The behavior for an out-of-range `RowDelta.row` is specified in `docs/reference/ipc.md` and the row-codec/renderer documentation as appropriate. - A test proves that an invalid row cannot silently leave the shadow in an unreported divergent state. - Resize-race tests continue to prove that valid rehydrate/damage bursts are accepted. - Receiver-side allocation limits remain enforced. ## Sources - `crates/felis-client-core/src/shadow.rs` (`write_row`) - `crates/felis-protocol/src/messages.rs` (grid limits) - `docs/reference/ipc.md` (semantic limits and corruption behavior)
Author
Owner

Direction agreed 2026-09-21 after three sol design-review rounds (final verdict PASS). Full plan and batch order: see the comment on #12.

  • #304 / #315 / #308 — one policy: a daemon-impossible Grid value returns
    ShadowError and closes only that attachment; reconnect/rehydrate recovers.
    Resize-race tolerance is kept only for row indices/widths within
    MAX_GRID_ROWS / MAX_GRID_COLS. Unresolved cluster/hyperlink handles in a
    decoded row are corruption; degraded rendering is removed. Id 0, over-cap
    ids and over-length text are rejected before allocation.
Direction agreed 2026-09-21 after three `sol` design-review rounds (final verdict PASS). Full plan and batch order: see the comment on #12. - **#304 / #315 / #308** — one policy: a daemon-impossible Grid value returns `ShadowError` and closes only that attachment; reconnect/rehydrate recovers. Resize-race tolerance is kept only for row indices/widths within `MAX_GRID_ROWS` / `MAX_GRID_COLS`. Unresolved cluster/hyperlink handles in a decoded row are corruption; degraded rendering is removed. Id 0, over-cap ids and over-length text are rejected before allocation.
Commenting is not possible because the repository is archived.
No description provided.