docs(wasm-client): say what the classification duplication actually costs

Review of the previous commit found its own doc overstating. It said "no host
anywhere ever sees the string to classify it" two paragraphs after saying this
is the same split `commit_regular_cell_value` makes -- and the TUI host does
classify its own local keystrokes there. The precise claim is about the WIRE:
`ModelCommand::SetCell` carries a `CellValue`, so nothing that arrives over a
frame is ever a string. A host's own keyboard is a different path.

Which exposes the part worth recording rather than smoothing over. The rule
genuinely lives in two places, because `improvise-command` is deliberately not
a dependency of this crate, and
`a_typed_string_is_classified_as_the_host_classifies_it` states that
obligation without discharging it -- it asserts three outcomes by hand instead
of comparing with the host's function, so the two can drift and stay green.
That is a_consolidation_that_keeps_its_compatibility_test_is_unfinished still
open one layer below what improvise-r4lo closed. Named in the doc and filed,
not fixed here: the real answer is a shared split in `improvise-protocol`,
which both crates already depend on, and that moves code in a crate this
ticket does not own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Edward Langley
2026-09-19 22:39:52 -07:00
parent d120596a10
commit fa40abe932
+24 -5
View File
@@ -65,10 +65,29 @@ fn rejection_message(rejection: &Rejection) -> String {
/// Rust rather than in the page's JavaScript is what makes it testable and
/// keeps one spelling of the rule per language boundary instead of two.
///
/// This is the **only** answer the client gives to "what does a typed string
/// mean" (improvise-r4lo). It holds on every deployment, including the worker:
/// a host's `App` is behind the frame boundary and receives a `CellValue`, so
/// no host anywhere ever sees the string to classify it.
/// This is the **only** answer the *client* gives to "what does a typed string
/// mean" (improvise-r4lo), and it holds on every deployment, including the
/// worker: a host's `App` sits behind the frame boundary, and what crosses that
/// boundary is `ModelCommand::SetCell { value: CellValue }`. So no host ever
/// classifies a string that arrived *over the wire* — there is none to
/// classify by the time it gets there.
///
/// Note the careful half of that. A host does classify its own **local**
/// keystrokes, in `commit_regular_cell_value`, which is why this is a
/// duplicate of that split rather than a call to it: `improvise-command` is
/// deliberately not a dependency of this crate (see `Cargo.toml` — reaching
/// it would drag `App`, `Workbook` and persistence into the browser bundle).
///
/// So this rule genuinely lives in two places, and
/// `a_typed_string_is_classified_as_the_host_classifies_it` states the
/// obligation without discharging it: it asserts the three outcomes by hand
/// rather than comparing with the host's function, so the two could drift and
/// stay green. That is
/// `doctrine(a_consolidation_that_keeps_its_compatibility_test_is_unfinished)`
/// still open, one layer below what improvise-r4lo closed — the real fix is a
/// shared split in `improvise-protocol`, which both crates already depend on.
/// Filed rather than done here, because it moves code in a crate this ticket
/// does not own.
///
/// `None` means "clear this cell", which is a different command, not a
/// different value — hence the `Option` rather than a `CellValue::Text("")`.
@@ -153,7 +172,7 @@ impl Client {
/// It takes the user's **string**, not a `CellValue`, and [`typed`] above
/// says why: the classification is unavoidably this side of the wire,
/// because `ModelCommand::SetCell` already carries a `CellValue` by the
/// time any host sees it. That is true of the worker deployment too — the
/// time any host sees it *over the wire*. That is true of the worker too — the
/// `App` there sits *behind* the frame boundary and is handed a value, not
/// a string — so there is no deployment for which a pre-classified entry
/// point would be the right one. A sibling taking a serialized `CellValue`