[v0.1/P1] Reject impossible Grid coordinates instead of normalizing them #315
Labels
No labels
priority/P0
priority/P1
priority/P2
release/v0.1.0
status/blocked
status/planned
type/bug
type/design
type/test-gap
type/tracker
No milestone
No project
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
natsukium/felis#315
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Parent: #12. Related: #304 (out-of-range row), #305 (drained-body validation), #308 (registry references).
Problem
Several daemon-impossible Grid values are silently normalized by the client instead of following the connection-local corruption policy:
ShadowScreen::write_rowtruncates a decoded row wider thanMAX_GRID_COLSafterdecode_rowhas accepted and allocated up to the row codec'su16count.ScreenBuffer::apply_scroll_directiveclampsregion_top/region_bottom, turnsn_rows = 0into 1, and caps an oversized count to the region height.ScreenBuffer::set_cursor_stateclamps coordinates outside the current grid.The IPC reference already says
GridScrolled.n_rows >= 1, bounds admitted geometry at 2048 × 2048, and treats an undecodable body or impossible authoritative state as corruption. Clamping these values can leave the shadow different from the daemon while reporting a healthy connection.This is distinct from a legitimate resize race. A
RowDeltawithin the protocol-wide bounds may temporarily be wider or lower than the client's current shadow and can grow it as documented. A value outside the protocol-wide bounds, or aScrolled/CursorStateimpossible under the ordered authoritative dimensions, has no honest race interpretation.Proposed resolution
Validate Grid semantic invariants before mutating the shadow and return
ShadowErrorfor daemon-impossible values. The client should close only that attachment; reconnect and rehydrate recover it without affecting the daemon session or other subscribers.At minimum:
MAX_GRID_COLSbefore allocating its cell vector;Scrolled.region_top <= region_bottom, both inside the current grid, and1 <= n_rows <= region_height;ViewportState.lines_from_bottom <= maxrather than accepting an impossible mirror silently.Keep the documented resize-race accommodation only for row indices and widths within
MAX_GRID_ROWS/MAX_GRID_COLS.Acceptance criteria
MAX_GRID_COLSis rejected beforeVec<Cell>reserves from the peer's count.RowDeltaraces still grow, pad, or trim within the published global bounds as explicitly documented.docs/reference/ipc.md, the row-codec reference, Grid explanation, andShadowErroragree.Principle check
Direction agreed 2026-09-21 after three
soldesign-review rounds (final verdict PASS). Full plan and batch order: see the comment on #12.ShadowErrorand 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 adecoded row are corruption; degraded rendering is removed. Id 0, over-cap
ids and over-length text are rejected before allocation.